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 SetSelection(wxListCtrl* listCtrl, int nIndex)
108 int nSize = listCtrl->GetItemCount();
109 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
110 for (int i = 0; i < nSize; i++)
111 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
114 int GetSelection(wxListCtrl* listCtrl)
116 int nSize = listCtrl->GetItemCount();
117 for (int i = 0; i < nSize; i++)
118 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
123 string HtmlEscape(const char* psz, bool fMultiLine=false)
126 for (const char* p = psz; *p; p++)
128 if (*p == '<') len += 4;
129 else if (*p == '>') len += 4;
130 else if (*p == '&') len += 5;
131 else if (*p == '"') len += 6;
132 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
133 else if (*p == '\n' && fMultiLine) len += 5;
139 for (const char* p = psz; *p; p++)
141 if (*p == '<') str += "<";
142 else if (*p == '>') str += ">";
143 else if (*p == '&') str += "&";
144 else if (*p == '"') str += """;
145 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
146 else if (*p == '\n' && fMultiLine) str += "<br>\n";
153 string HtmlEscape(const string& str, bool fMultiLine=false)
155 return HtmlEscape(str.c_str(), fMultiLine);
158 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
160 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
164 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
167 return wxMessageBox(message, caption, style, parent, x, y);
169 if (wxThread::IsMain() || fDaemon)
171 return wxMessageBox(message, caption, style, parent, x, y);
177 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
185 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
187 if (nFeeRequired == 0 || fDaemon)
189 string strMessage = strprintf(
190 _("This transaction is over the size limit. You can still send it for a fee of %s, "
191 "which goes to the nodes that process your transaction and helps to support the network. "
192 "Do you want to pay the fee?"),
193 FormatMoney(nFeeRequired).c_str());
194 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
197 void CalledSetStatusBar(const string& strText, int nField)
199 if (nField == 0 && IsLockdown())
201 if (pframeMain && pframeMain->m_statusBar)
202 pframeMain->m_statusBar->SetStatusText(strText, nField);
205 void SetDefaultReceivingAddress(const string& strAddress)
207 // Update main window address and database
208 if (pframeMain == NULL)
210 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
213 if (!AddressToHash160(strAddress, hash160))
215 if (!mapPubKeys.count(hash160))
217 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
218 pframeMain->m_textCtrlAddress->SetValue(strAddress);
231 //////////////////////////////////////////////////////////////////////////////
236 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
238 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
240 // Set initially selected page
241 wxNotebookEvent event;
242 event.SetSelection(0);
243 OnNotebookPageChanged(event);
244 m_notebook->ChangeSelection(0);
247 fRefreshListCtrl = false;
248 fRefreshListCtrlRunning = false;
249 fOnSetFocusAddress = false;
251 m_choiceFilter->SetSelection(0);
252 double dResize = 1.0;
254 SetIcon(wxICON(bitcoin));
256 SetIcon(bitcoin80_xpm);
257 SetBackgroundColour(m_toolBar->GetBackgroundColour());
258 wxFont fontTmp = m_staticText41->GetFont();
259 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
260 m_staticTextBalance->SetFont(fontTmp);
261 m_staticTextBalance->SetSize(140, 17);
262 // resize to fit ubuntu's huge default font
264 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
266 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
267 m_listCtrl->SetFocus();
268 ptaskbaricon = new CMyTaskBarIcon();
270 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
271 // to their standard places, leaving these menus empty.
272 GetMenuBar()->Remove(2); // remove Help menu
273 GetMenuBar()->Remove(0); // remove File menu
276 // Init column headers
277 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
278 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
284 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
285 foreach(wxListCtrl* p, pplistCtrl)
287 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
288 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
289 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
290 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
291 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
292 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
293 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
297 int pnWidths[3] = { -100, 88, 300 };
299 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
300 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
302 m_statusBar->SetFieldsCount(3, pnWidths);
304 // Fill your address text box
305 vector<unsigned char> vchPubKey;
306 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
307 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
309 // Fill listctrl with wallet transactions
313 CMainFrame::~CMainFrame()
320 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
323 nPage = event.GetSelection();
326 m_listCtrl = m_listCtrlAll;
327 fShowGenerated = true;
329 fShowReceived = true;
331 else if (nPage == SENTRECEIVED)
333 m_listCtrl = m_listCtrlSentReceived;
334 fShowGenerated = false;
336 fShowReceived = true;
338 else if (nPage == SENT)
340 m_listCtrl = m_listCtrlSent;
341 fShowGenerated = false;
343 fShowReceived = false;
345 else if (nPage == RECEIVED)
347 m_listCtrl = m_listCtrlReceived;
348 fShowGenerated = false;
350 fShowReceived = true;
353 m_listCtrl->SetFocus();
356 void CMainFrame::OnClose(wxCloseEvent& event)
358 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
360 // Divert close to minimize
362 fClosedToTray = true;
368 CreateThread(Shutdown, NULL);
372 void CMainFrame::OnIconize(wxIconizeEvent& event)
375 // Hide the task bar button when minimized.
376 // Event is sent when the frame is minimized or restored.
377 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
378 // to get rid of the deprecated warning. Just ignore it.
379 if (!event.Iconized())
380 fClosedToTray = false;
381 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
382 if (mapArgs.count("-minimizetotray")) {
384 // The tray icon sometimes disappears on ubuntu karmic
385 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
386 // Reports of CPU peg on 64-bit linux
387 if (fMinimizeToTray && event.Iconized())
388 fClosedToTray = true;
389 Show(!fClosedToTray);
390 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
391 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
396 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
400 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
401 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
404 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
406 // Hidden columns not resizeable
407 if (event.GetColumn() <= 1 && !fDebug)
413 int CMainFrame::GetSortIndex(const string& strSort)
418 // The wx generic listctrl implementation used on GTK doesn't sort,
419 // so we have to do it ourselves. Remember, we sort in reverse order.
420 // In the wx generic implementation, they store the list of items
421 // in a vector, so indexed lookups are fast, but inserts are slower
422 // the closer they are to the top.
424 int high = m_listCtrl->GetItemCount();
427 int mid = low + ((high - low) / 2);
428 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
437 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
439 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
440 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
443 if (!fNew && nIndex == -1)
445 string strHash = " " + hashKey.ToString();
446 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
447 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
451 // fNew is for blind insert, only use if you're sure it's new
452 if (fNew || nIndex == -1)
454 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
458 // If sort key changed, must delete and reinsert to make it relocate
459 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
461 m_listCtrl->DeleteItem(nIndex);
462 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
466 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
467 m_listCtrl->SetItem(nIndex, 2, str2);
468 m_listCtrl->SetItem(nIndex, 3, str3);
469 m_listCtrl->SetItem(nIndex, 4, str4);
470 m_listCtrl->SetItem(nIndex, 5, str5);
471 m_listCtrl->SetItem(nIndex, 6, str6);
472 m_listCtrl->SetItemData(nIndex, nData);
475 bool CMainFrame::DeleteLine(uint256 hashKey)
477 long nData = *(long*)&hashKey;
481 string strHash = " " + hashKey.ToString();
482 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
483 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
487 m_listCtrl->DeleteItem(nIndex);
492 string FormatTxStatus(const CWalletTx& wtx)
497 if (wtx.nLockTime < 500000000)
498 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
500 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
504 int nDepth = wtx.GetDepthInMainChain();
505 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
506 return strprintf(_("%d/offline?"), nDepth);
508 return strprintf(_("%d/unconfirmed"), nDepth);
510 return strprintf(_("%d confirmations"), nDepth);
514 string SingleLine(const string& strIn)
517 bool fOneSpace = false;
518 foreach(unsigned char c, strIn)
526 if (fOneSpace && !strOut.empty())
535 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
537 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
538 int64 nCredit = wtx.GetCredit(true);
539 int64 nDebit = wtx.GetDebit();
540 int64 nNet = nCredit - nDebit;
541 uint256 hash = wtx.GetHash();
542 string strStatus = FormatTxStatus(wtx);
543 map<string, string> mapValue = wtx.mapValue;
544 wtx.nLinesDisplayed = 1;
548 if (wtx.IsCoinBase())
550 // Don't show generated coin until confirmed by at least one block after it
551 // so we don't get the user's hopes up until it looks like it's probably accepted.
553 // It is not an error when generated blocks are not accepted. By design,
554 // some percentage of blocks, like 10% or more, will end up not accepted.
555 // This is the normal mechanism by which the network copes with latency.
557 // We display regular transactions right away before any confirmation
558 // because they can always get into some block eventually. Generated coins
559 // are special because if their block is not accepted, they are not valid.
561 if (wtx.GetDepthInMainChain() < 2)
563 wtx.nLinesDisplayed = 0;
571 // Find the block the tx is in
572 CBlockIndex* pindex = NULL;
573 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
574 if (mi != mapBlockIndex.end())
575 pindex = (*mi).second;
577 // Sort order, unrecorded transactions sort to the top
578 string strSort = strprintf("%010d-%01d-%010u",
579 (pindex ? pindex->nHeight : INT_MAX),
580 (wtx.IsCoinBase() ? 1 : 0),
584 if (nNet > 0 || wtx.IsCoinBase())
589 string strDescription;
590 if (wtx.IsCoinBase())
593 strDescription = _("Generated");
596 int64 nUnmatured = 0;
597 foreach(const CTxOut& txout, wtx.vout)
598 nUnmatured += txout.GetCredit();
599 if (wtx.IsInMainChain())
601 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
603 // Check if the block was requested by anyone
604 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
605 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
609 strDescription = _("Generated (not accepted)");
613 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
615 // Received by IP connection
618 if (!mapValue["from"].empty())
619 strDescription += _("From: ") + mapValue["from"];
620 if (!mapValue["message"].empty())
622 if (!strDescription.empty())
623 strDescription += " - ";
624 strDescription += mapValue["message"];
629 // Received by Bitcoin Address
632 foreach(const CTxOut& txout, wtx.vout)
636 vector<unsigned char> vchPubKey;
637 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
639 CRITICAL_BLOCK(cs_mapAddressBook)
641 //strDescription += _("Received payment to ");
642 //strDescription += _("Received with address ");
643 strDescription += _("Received with: ");
644 string strAddress = PubKeyToAddress(vchPubKey);
645 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
646 if (mi != mapAddressBook.end() && !(*mi).second.empty())
648 string strLabel = (*mi).second;
649 strDescription += strAddress.substr(0,12) + "... ";
650 strDescription += "(" + strLabel + ")";
653 strDescription += strAddress;
661 InsertLine(fNew, nIndex, hash, strSort,
663 nTime ? DateTimeStr(nTime) : "",
664 SingleLine(strDescription),
666 FormatMoney(nNet, true));
670 bool fAllFromMe = true;
671 foreach(const CTxIn& txin, wtx.vin)
672 fAllFromMe = fAllFromMe && txin.IsMine();
674 bool fAllToMe = true;
675 foreach(const CTxOut& txout, wtx.vout)
676 fAllToMe = fAllToMe && txout.IsMine();
678 if (fAllFromMe && fAllToMe)
681 int64 nValue = wtx.vout[0].nValue;
682 InsertLine(fNew, nIndex, hash, strSort,
684 nTime ? DateTimeStr(nTime) : "",
685 _("Payment to yourself"),
688 /// issue: can't tell which is the payment and which is the change anymore
689 // FormatMoney(nNet - nValue, true),
690 // FormatMoney(nValue, true));
700 int64 nTxFee = nDebit - wtx.GetValueOut();
701 wtx.nLinesDisplayed = 0;
702 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
704 const CTxOut& txout = wtx.vout[nOut];
709 if (!mapValue["to"].empty())
712 strAddress = mapValue["to"];
716 // Sent to Bitcoin Address
718 if (ExtractHash160(txout.scriptPubKey, hash160))
719 strAddress = Hash160ToAddress(hash160);
722 string strDescription = _("To: ");
723 CRITICAL_BLOCK(cs_mapAddressBook)
724 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
725 strDescription += mapAddressBook[strAddress] + " ";
726 strDescription += strAddress;
727 if (!mapValue["message"].empty())
729 if (!strDescription.empty())
730 strDescription += " - ";
731 strDescription += mapValue["message"];
734 int64 nValue = txout.nValue;
741 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
743 nTime ? DateTimeStr(nTime) : "",
744 SingleLine(strDescription),
745 FormatMoney(-nValue, true),
747 wtx.nLinesDisplayed++;
753 // Mixed debit transaction, can't break down payees
755 bool fAllMine = true;
756 foreach(const CTxOut& txout, wtx.vout)
757 fAllMine = fAllMine && txout.IsMine();
758 foreach(const CTxIn& txin, wtx.vin)
759 fAllMine = fAllMine && txin.IsMine();
761 InsertLine(fNew, nIndex, hash, strSort,
763 nTime ? DateTimeStr(nTime) : "",
765 FormatMoney(nNet, true),
773 void CMainFrame::RefreshListCtrl()
775 fRefreshListCtrl = true;
779 void CMainFrame::OnIdle(wxIdleEvent& event)
781 if (fRefreshListCtrl)
783 // Collect list of wallet transactions and sort newest first
784 bool fEntered = false;
785 vector<pair<unsigned int, uint256> > vSorted;
786 TRY_CRITICAL_BLOCK(cs_mapWallet)
788 printf("RefreshListCtrl starting\n");
790 fRefreshListCtrl = false;
791 vWalletUpdated.clear();
793 // Do the newest transactions first
794 vSorted.reserve(mapWallet.size());
795 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
797 const CWalletTx& wtx = (*it).second;
798 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
799 vSorted.push_back(make_pair(nTime, (*it).first));
801 m_listCtrl->DeleteAllItems();
806 sort(vSorted.begin(), vSorted.end());
809 for (int i = 0; i < vSorted.size();)
813 bool fEntered = false;
814 TRY_CRITICAL_BLOCK(cs_mapWallet)
817 uint256& hash = vSorted[i++].second;
818 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
819 if (mi != mapWallet.end())
820 InsertTransaction((*mi).second, true);
822 if (!fEntered || i == 100 || i % 500 == 0)
826 printf("RefreshListCtrl done\n");
828 // Update transaction total display
833 // Check for time updates
834 static int64 nLastTime;
835 if (GetTime() > nLastTime + 30)
837 TRY_CRITICAL_BLOCK(cs_mapWallet)
839 nLastTime = GetTime();
840 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
842 CWalletTx& wtx = (*it).second;
843 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
844 InsertTransaction(wtx, false);
851 void CMainFrame::RefreshStatusColumn()
854 static CBlockIndex* pindexLastBest;
855 static unsigned int nLastRefreshed;
857 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
858 if (nTop == nLastTop && pindexLastBest == pindexBest)
861 TRY_CRITICAL_BLOCK(cs_mapWallet)
864 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
866 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
868 // If no updates, only need to do the part that moved onto the screen
869 if (nStart >= nLastTop && nStart < nLastTop + 100)
870 nStart = nLastTop + 100;
871 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
875 pindexLastBest = pindexBest;
876 nLastRefreshed = nListViewUpdated;
878 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
880 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
881 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
882 if (mi == mapWallet.end())
884 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
887 CWalletTx& wtx = (*mi).second;
888 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
890 if (!InsertTransaction(wtx, false, nIndex))
891 m_listCtrl->DeleteItem(nIndex--);
894 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
899 void CMainFrame::OnPaint(wxPaintEvent& event)
910 unsigned int nNeedRepaint = 0;
911 unsigned int nLastRepaint = 0;
912 int64 nLastRepaintTime = 0;
913 int64 nRepaintInterval = 500;
915 void ThreadDelayedRepaint(void* parg)
919 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
921 nLastRepaint = nNeedRepaint;
924 printf("DelayedRepaint\n");
926 pframeMain->fRefresh = true;
927 pframeMain->GetEventHandler()->AddPendingEvent(event);
930 Sleep(nRepaintInterval);
934 void MainFrameRepaint()
936 // This is called by network code that shouldn't access pframeMain
937 // directly because it could still be running after the UI is closed.
940 // Don't repaint too often
941 static int64 nLastRepaintRequest;
942 if (GetTimeMillis() - nLastRepaintRequest < 100)
947 nLastRepaintRequest = GetTimeMillis();
949 printf("MainFrameRepaint\n");
951 pframeMain->fRefresh = true;
952 pframeMain->GetEventHandler()->AddPendingEvent(event);
956 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
958 // Skip lets the listctrl do the paint, we're just hooking the message
962 ptaskbaricon->UpdateTooltip();
967 static int nTransactionCount;
968 bool fPaintedBalance = false;
969 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
971 nLastRepaint = nNeedRepaint;
972 nLastRepaintTime = GetTimeMillis();
974 // Update listctrl contents
975 if (!vWalletUpdated.empty())
977 TRY_CRITICAL_BLOCK(cs_mapWallet)
980 if (m_listCtrl->GetItemCount())
981 strTop = (string)m_listCtrl->GetItemText(0);
982 foreach(uint256 hash, vWalletUpdated)
984 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
985 if (mi != mapWallet.end())
986 InsertTransaction((*mi).second, false);
988 vWalletUpdated.clear();
989 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
990 m_listCtrl->ScrollList(0, INT_MIN/2);
995 TRY_CRITICAL_BLOCK(cs_mapWallet)
997 fPaintedBalance = true;
998 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1000 // Count hidden and multi-line transactions
1001 nTransactionCount = 0;
1002 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1004 CWalletTx& wtx = (*it).second;
1005 nTransactionCount += wtx.nLinesDisplayed;
1009 if (!vWalletUpdated.empty() || !fPaintedBalance)
1012 // Update status column of visible items only
1013 RefreshStatusColumn();
1015 // Update status bar
1016 static bool fPrevLockdown;
1018 m_statusBar->SetStatusText(string(" ") + _("WARNING: Displayed transactions may not be correct! You may need to upgrade, or other nodes may need to upgrade."), 0);
1019 else if (fPrevLockdown)
1020 m_statusBar->SetStatusText("", 0);
1021 fPrevLockdown = IsLockdown();
1024 if (fGenerateBitcoins)
1025 strGen = _(" Generating");
1026 if (fGenerateBitcoins && vNodes.empty())
1027 strGen = _("(not connected)");
1028 m_statusBar->SetStatusText(strGen, 1);
1030 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
1031 m_statusBar->SetStatusText(strStatus, 2);
1033 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
1034 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
1036 // Update receiving address
1037 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1038 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1039 m_textCtrlAddress->SetValue(strDefaultAddress);
1043 void UIThreadCall(boost::function0<void> fn)
1045 // Call this with a function object created with bind.
1046 // bind needs all parameters to match the function's expected types
1047 // and all default parameters specified. Some examples:
1048 // UIThreadCall(bind(wxBell));
1049 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1050 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1053 wxCommandEvent event(wxEVT_UITHREADCALL);
1054 event.SetClientData((void*)new boost::function0<void>(fn));
1055 pframeMain->GetEventHandler()->AddPendingEvent(event);
1059 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1061 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1066 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1072 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1074 // Options->Generate Coins
1075 GenerateBitcoins(event.IsChecked());
1078 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1080 event.Check(fGenerateBitcoins);
1083 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1085 // Options->Your Receiving Addresses
1086 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1087 if (!dialog.ShowModal())
1091 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1094 COptionsDialog dialog(this);
1098 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1101 CAboutDialog dialog(this);
1105 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1108 CSendDialog dialog(this);
1112 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1114 // Toolbar: Address Book
1115 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1116 if (dialogAddr.ShowModal() == 2)
1119 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1120 dialogSend.ShowModal();
1124 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1126 // Automatically select-all when entering window
1128 m_textCtrlAddress->SetSelection(-1, -1);
1129 fOnSetFocusAddress = true;
1132 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1135 if (fOnSetFocusAddress)
1136 m_textCtrlAddress->SetSelection(-1, -1);
1137 fOnSetFocusAddress = false;
1140 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1143 CGetTextFromUserDialog dialog(this,
1144 _("New Receiving Address"),
1145 _("You should use a new address for each payment you receive.\n\nLabel"),
1147 if (!dialog.ShowModal())
1149 string strName = dialog.GetValue();
1152 string strAddress = PubKeyToAddress(GenerateNewKey());
1155 SetAddressBookName(strAddress, strName);
1156 SetDefaultReceivingAddress(strAddress);
1159 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1161 // Copy address box to clipboard
1162 if (wxTheClipboard->Open())
1164 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1165 wxTheClipboard->Close();
1169 void CMainFrame::OnListItemActivated(wxListEvent& event)
1171 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1173 CRITICAL_BLOCK(cs_mapWallet)
1175 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1176 if (mi == mapWallet.end())
1178 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1183 CTxDetailsDialog dialog(this, wtx);
1185 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1194 //////////////////////////////////////////////////////////////////////////////
1199 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1201 CRITICAL_BLOCK(cs_mapAddressBook)
1204 strHTML.reserve(4000);
1205 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1207 int64 nTime = wtx.GetTxTime();
1208 int64 nCredit = wtx.GetCredit();
1209 int64 nDebit = wtx.GetDebit();
1210 int64 nNet = nCredit - nDebit;
1214 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1215 int nRequests = wtx.GetRequestCount();
1216 if (nRequests != -1)
1219 strHTML += _(", has not been successfully broadcast yet");
1220 else if (nRequests == 1)
1221 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1223 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1227 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1233 if (wtx.IsCoinBase())
1235 strHTML += _("<b>Source:</b> Generated<br>");
1237 else if (!wtx.mapValue["from"].empty())
1239 // Online transaction
1240 if (!wtx.mapValue["from"].empty())
1241 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1245 // Offline transaction
1249 foreach(const CTxOut& txout, wtx.vout)
1253 vector<unsigned char> vchPubKey;
1254 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1256 string strAddress = PubKeyToAddress(vchPubKey);
1257 if (mapAddressBook.count(strAddress))
1259 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1260 strHTML += _("<b>To:</b> ");
1261 strHTML += HtmlEscape(strAddress);
1262 if (!mapAddressBook[strAddress].empty())
1263 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1265 strHTML += _(" (yours)");
1280 if (!wtx.mapValue["to"].empty())
1282 // Online transaction
1283 strAddress = wtx.mapValue["to"];
1284 strHTML += _("<b>To:</b> ");
1285 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1286 strHTML += mapAddressBook[strAddress] + " ";
1287 strHTML += HtmlEscape(strAddress) + "<br>";
1294 if (wtx.IsCoinBase() && nCredit == 0)
1299 int64 nUnmatured = 0;
1300 foreach(const CTxOut& txout, wtx.vout)
1301 nUnmatured += txout.GetCredit();
1302 strHTML += _("<b>Credit:</b> ");
1303 if (wtx.IsInMainChain())
1304 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1306 strHTML += _("(not accepted)");
1314 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1318 bool fAllFromMe = true;
1319 foreach(const CTxIn& txin, wtx.vin)
1320 fAllFromMe = fAllFromMe && txin.IsMine();
1322 bool fAllToMe = true;
1323 foreach(const CTxOut& txout, wtx.vout)
1324 fAllToMe = fAllToMe && txout.IsMine();
1331 foreach(const CTxOut& txout, wtx.vout)
1336 if (wtx.mapValue["to"].empty())
1338 // Offline transaction
1340 if (ExtractHash160(txout.scriptPubKey, hash160))
1342 string strAddress = Hash160ToAddress(hash160);
1343 strHTML += _("<b>To:</b> ");
1344 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1345 strHTML += mapAddressBook[strAddress] + " ";
1346 strHTML += strAddress;
1351 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1357 /// issue: can't tell which is the payment and which is the change anymore
1358 //int64 nValue = wtx.vout[0].nValue;
1359 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1360 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1363 int64 nTxFee = nDebit - wtx.GetValueOut();
1365 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1370 // Mixed debit transaction
1372 foreach(const CTxIn& txin, wtx.vin)
1374 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1375 foreach(const CTxOut& txout, wtx.vout)
1377 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1381 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1387 if (!wtx.mapValue["message"].empty())
1388 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1390 if (wtx.IsCoinBase())
1391 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>";
1399 strHTML += "<hr><br>debug print<br><br>";
1400 foreach(const CTxIn& txin, wtx.vin)
1402 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1403 foreach(const CTxOut& txout, wtx.vout)
1405 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1407 strHTML += "<b>Inputs:</b><br>";
1408 CRITICAL_BLOCK(cs_mapWallet)
1410 foreach(const CTxIn& txin, wtx.vin)
1412 COutPoint prevout = txin.prevout;
1413 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1414 if (mi != mapWallet.end())
1416 const CWalletTx& prev = (*mi).second;
1417 if (prevout.n < prev.vout.size())
1419 strHTML += HtmlEscape(prev.ToString(), true);
1420 strHTML += " " + FormatTxStatus(prev) + ", ";
1421 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1427 strHTML += "<br><hr><br><b>Transaction:</b><br>";
1428 strHTML += HtmlEscape(wtx.ToString(), true);
1433 strHTML += "</font></html>";
1434 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1435 m_htmlWin->SetPage(strHTML);
1436 m_buttonOK->SetFocus();
1440 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1451 //////////////////////////////////////////////////////////////////////////////
1457 string StartupShortcutPath()
1459 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1462 bool GetStartOnSystemStartup()
1464 return filesystem::exists(StartupShortcutPath().c_str());
1467 void SetStartOnSystemStartup(bool fAutoStart)
1469 // If the shortcut exists already, remove it for updating
1470 remove(StartupShortcutPath().c_str());
1476 // Get a pointer to the IShellLink interface.
1477 IShellLink* psl = NULL;
1478 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1479 CLSCTX_INPROC_SERVER, IID_IShellLink,
1480 reinterpret_cast<void**>(&psl));
1482 if (SUCCEEDED(hres))
1484 // Get the current executable path
1485 TCHAR pszExePath[MAX_PATH];
1486 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1488 // Set the path to the shortcut target
1489 psl->SetPath(pszExePath);
1490 PathRemoveFileSpec(pszExePath);
1491 psl->SetWorkingDirectory(pszExePath);
1492 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1494 // Query IShellLink for the IPersistFile interface for
1495 // saving the shortcut in persistent storage.
1496 IPersistFile* ppf = NULL;
1497 hres = psl->QueryInterface(IID_IPersistFile,
1498 reinterpret_cast<void**>(&ppf));
1499 if (SUCCEEDED(hres))
1501 WCHAR pwsz[MAX_PATH];
1502 // Ensure that the string is ANSI.
1503 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1504 // Save the link by calling IPersistFile::Save.
1505 hres = ppf->Save(pwsz, TRUE);
1514 #elif defined(__WXGTK__)
1516 // Follow the Desktop Application Autostart Spec:
1517 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1519 boost::filesystem::path GetAutostartDir()
1521 namespace fs = boost::filesystem;
1523 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1524 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1525 char* pszHome = getenv("HOME");
1526 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1530 boost::filesystem::path GetAutostartFilePath()
1532 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1535 bool GetStartOnSystemStartup()
1537 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1538 if (!optionFile.good())
1540 // Scan through file for "Hidden=true":
1542 while (!optionFile.eof())
1544 getline(optionFile, line);
1545 if (line.find("Hidden") != string::npos &&
1546 line.find("true") != string::npos)
1554 void SetStartOnSystemStartup(bool fAutoStart)
1558 unlink(GetAutostartFilePath().native_file_string().c_str());
1562 char pszExePath[MAX_PATH+1];
1563 memset(pszExePath, 0, sizeof(pszExePath));
1564 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1567 boost::filesystem::create_directories(GetAutostartDir());
1569 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1570 if (!optionFile.good())
1572 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1575 // Write a bitcoin.desktop file to the autostart directory:
1576 optionFile << "[Desktop Entry]\n";
1577 optionFile << "Type=Application\n";
1578 optionFile << "Name=Bitcoin\n";
1579 optionFile << "Exec=" << pszExePath << "\n";
1580 optionFile << "Terminal=false\n";
1581 optionFile << "Hidden=false\n";
1587 // TODO: OSX startup stuff; see:
1588 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1590 bool GetStartOnSystemStartup() { return false; }
1591 void SetStartOnSystemStartup(bool fAutoStart) { }
1600 //////////////////////////////////////////////////////////////////////////////
1605 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1607 // Set up list box of page choices
1608 m_listBox->Append(_("Main"));
1609 //m_listBox->Append(_("Test 2"));
1610 m_listBox->SetSelection(0);
1612 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1613 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1614 if (!mapArgs.count("-minimizetotray"))
1616 // Minimize to tray is just too buggy on Linux
1617 fMinimizeToTray = false;
1618 m_checkBoxMinimizeToTray->SetValue(false);
1619 m_checkBoxMinimizeToTray->Enable(false);
1620 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1623 #ifdef __WXMAC_OSX__
1624 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1628 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1629 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1630 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1631 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1632 int nProcessors = wxThread::GetCPUCount();
1633 if (nProcessors < 1)
1635 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1636 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1637 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1638 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1639 m_checkBoxUseProxy->SetValue(fUseProxy);
1640 m_textCtrlProxyIP->Enable(fUseProxy);
1641 m_textCtrlProxyPort->Enable(fUseProxy);
1642 m_staticTextProxyIP->Enable(fUseProxy);
1643 m_staticTextProxyPort->Enable(fUseProxy);
1644 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1645 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1647 m_buttonOK->SetFocus();
1650 void COptionsDialog::SelectPage(int nPage)
1652 m_panelMain->Show(nPage == 0);
1653 m_panelTest2->Show(nPage == 1);
1655 m_scrolledWindow->Layout();
1656 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1659 void COptionsDialog::OnListBox(wxCommandEvent& event)
1661 SelectPage(event.GetSelection());
1664 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1667 int64 nTmp = nTransactionFee;
1668 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1669 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1672 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1674 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1677 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1679 m_textCtrlProxyIP->Enable(event.IsChecked());
1680 m_textCtrlProxyPort->Enable(event.IsChecked());
1681 m_staticTextProxyIP->Enable(event.IsChecked());
1682 m_staticTextProxyPort->Enable(event.IsChecked());
1685 CAddress COptionsDialog::GetProxyAddr()
1687 // Be careful about byte order, addr.ip and addr.port are big endian
1688 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1689 if (addr.ip == INADDR_NONE)
1690 addr.ip = addrProxy.ip;
1691 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1692 addr.port = htons(nPort);
1693 if (nPort <= 0 || nPort > USHRT_MAX)
1694 addr.port = addrProxy.port;
1698 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1701 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1702 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1706 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1708 OnButtonApply(event);
1712 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1717 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1721 int64 nPrevTransactionFee = nTransactionFee;
1722 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1723 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1725 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1726 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1728 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1729 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1731 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1733 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1734 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1736 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1737 GenerateBitcoins(fGenerateBitcoins);
1739 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1741 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1742 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1745 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1747 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1748 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1749 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1752 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1754 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1755 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1758 fUseProxy = m_checkBoxUseProxy->GetValue();
1759 walletdb.WriteSetting("fUseProxy", fUseProxy);
1761 addrProxy = GetProxyAddr();
1762 walletdb.WriteSetting("addrProxy", addrProxy);
1770 //////////////////////////////////////////////////////////////////////////////
1775 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1777 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer));
1779 // Change (c) into UTF-8 or ANSI copyright symbol
1780 wxString str = m_staticTextMain->GetLabel();
1782 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1784 str.Replace("(c)", "\xA9");
1786 m_staticTextMain->SetLabel(str);
1788 // Resize on Linux to make the window fit the text.
1789 // The text was wrapped manually rather than using the Wrap setting because
1790 // the wrap would be too small on Linux and it can't be changed at this point.
1791 wxFont fontTmp = m_staticTextMain->GetFont();
1792 if (fontTmp.GetPointSize() > 8);
1793 fontTmp.SetPointSize(8);
1794 m_staticTextMain->SetFont(fontTmp);
1795 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1799 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1809 //////////////////////////////////////////////////////////////////////////////
1814 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1817 m_textCtrlAddress->SetValue(strAddress);
1818 m_choiceTransferType->SetSelection(0);
1819 m_bitmapCheckMark->Show(false);
1820 fEnabledPrev = true;
1821 m_textCtrlAddress->SetFocus();
1822 //// todo: should add a display of your balance for convenience
1824 wxFont fontTmp = m_staticTextInstructions->GetFont();
1825 if (fontTmp.GetPointSize() > 9);
1826 fontTmp.SetPointSize(9);
1827 m_staticTextInstructions->SetFont(fontTmp);
1833 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1836 wxCommandEvent event;
1837 OnTextAddress(event);
1839 // Fixup the tab order
1840 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1841 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1845 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1849 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1850 m_bitmapCheckMark->Show(fBitcoinAddress);
1852 // Grey out message if bitcoin address
1853 bool fEnable = !fBitcoinAddress;
1854 m_staticTextFrom->Enable(fEnable);
1855 m_textCtrlFrom->Enable(fEnable);
1856 m_staticTextMessage->Enable(fEnable);
1857 m_textCtrlMessage->Enable(fEnable);
1858 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1859 if (!fEnable && fEnabledPrev)
1861 strFromSave = m_textCtrlFrom->GetValue();
1862 strMessageSave = m_textCtrlMessage->GetValue();
1863 m_textCtrlFrom->SetValue(_("n/a"));
1864 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1866 else if (fEnable && !fEnabledPrev)
1868 m_textCtrlFrom->SetValue(strFromSave);
1869 m_textCtrlMessage->SetValue(strMessageSave);
1871 fEnabledPrev = fEnable;
1874 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1876 // Reformat the amount
1878 if (m_textCtrlAmount->GetValue().Trim().empty())
1881 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1882 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1885 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1887 // Open address book
1888 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1889 if (dialog.ShowModal())
1890 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1893 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1895 // Copy clipboard to address box
1896 if (wxTheClipboard->Open())
1898 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1900 wxTextDataObject data;
1901 wxTheClipboard->GetData(data);
1902 m_textCtrlAddress->SetValue(data.GetText());
1904 wxTheClipboard->Close();
1908 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1911 string strAddress = (string)m_textCtrlAddress->GetValue();
1915 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1917 wxMessageBox(_("Error in amount "), _("Send Coins"));
1920 if (nValue > GetBalance())
1922 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1925 if (nValue + nTransactionFee > GetBalance())
1927 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1931 // Parse bitcoin address
1933 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1935 if (fBitcoinAddress)
1937 // Send to bitcoin address
1938 CScript scriptPubKey;
1939 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1941 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1943 wxMessageBox(_("Payment sent "), _("Sending..."));
1944 else if (strError != "ABORTED")
1945 wxMessageBox(strError + " ", _("Sending..."));
1950 CAddress addr(strAddress);
1951 if (!addr.IsValid())
1953 wxMessageBox(_("Invalid address "), _("Send Coins"));
1958 wtx.mapValue["to"] = strAddress;
1959 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1960 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1962 // Send to IP address
1963 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1964 if (!pdialog->ShowModal())
1968 CRITICAL_BLOCK(cs_mapAddressBook)
1969 if (!mapAddressBook.count(strAddress))
1970 SetAddressBookName(strAddress, "");
1975 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1986 //////////////////////////////////////////////////////////////////////////////
1991 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
1996 start = wxDateTime::UNow();
1997 memset(pszStatus, 0, sizeof(pszStatus));
2004 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2007 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2008 m_textCtrlStatus->SetValue("");
2010 CreateThread(SendingDialogStartTransfer, this);
2013 CSendingDialog::~CSendingDialog()
2015 printf("~CSendingDialog()\n");
2018 void CSendingDialog::Close()
2020 // Last one out turn out the lights.
2021 // fWorkDone signals that work side is done and UI thread should call destroy.
2022 // fUIDone signals that UI window has closed and work thread should call destroy.
2023 // This allows the window to disappear and end modality when cancelled
2024 // without making the user wait for ConnectNode to return. The dialog object
2025 // hangs around in the background until the work thread exits.
2036 void CSendingDialog::OnClose(wxCloseEvent& event)
2038 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2045 wxCommandEvent cmdevent;
2046 OnButtonCancel(cmdevent);
2050 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2056 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2062 void CSendingDialog::OnPaint(wxPaintEvent& event)
2065 if (strlen(pszStatus) > 130)
2066 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2068 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2069 m_staticTextSending->SetFocus();
2071 m_buttonCancel->Enable(false);
2074 m_buttonOK->Enable(true);
2075 m_buttonOK->SetFocus();
2076 m_buttonCancel->Enable(false);
2078 if (fAbort && fCanCancel && IsShown())
2080 strcpy(pszStatus, _("CANCELLED"));
2081 m_buttonOK->Enable(true);
2082 m_buttonOK->SetFocus();
2083 m_buttonCancel->Enable(false);
2084 m_buttonCancel->SetLabel(_("Cancelled"));
2086 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2092 // Everything from here on is not in the UI thread and must only communicate
2093 // with the rest of the dialog through variables and calling repaint.
2096 void CSendingDialog::Repaint()
2100 GetEventHandler()->AddPendingEvent(event);
2103 bool CSendingDialog::Status()
2110 if (fAbort && fCanCancel)
2112 memset(pszStatus, 0, 10);
2113 strcpy(pszStatus, _("CANCELLED"));
2121 bool CSendingDialog::Status(const string& str)
2126 // This can be read by the UI thread at any time,
2127 // so copy in a way that can be read cleanly at all times.
2128 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2129 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2135 bool CSendingDialog::Error(const string& str)
2139 Status(string(_("Error: ")) + str);
2143 void SendingDialogStartTransfer(void* parg)
2145 ((CSendingDialog*)parg)->StartTransfer();
2148 void CSendingDialog::StartTransfer()
2150 // Make sure we have enough money
2151 if (nPrice + nTransactionFee > GetBalance())
2153 Error(_("Insufficient funds"));
2157 // We may have connected already for product details
2158 if (!Status(_("Connecting...")))
2160 CNode* pnode = ConnectNode(addr, 15 * 60);
2163 Error(_("Unable to connect"));
2167 // Send order to seller, with response going to OnReply2 via event handler
2168 if (!Status(_("Requesting public key...")))
2170 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2173 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2175 ((CSendingDialog*)parg)->OnReply2(vRecv);
2178 void CSendingDialog::OnReply2(CDataStream& vRecv)
2180 if (!Status(_("Received public key...")))
2183 CScript scriptPubKey;
2191 vRecv >> strMessage;
2192 Error(_("Transfer was not accepted"));
2193 //// todo: enlarge the window and enable a hidden white box to put seller's message
2196 vRecv >> scriptPubKey;
2200 //// what do we want to do about this?
2201 Error(_("Invalid response received"));
2205 // Pause to give the user a chance to cancel
2206 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2213 CRITICAL_BLOCK(cs_main)
2216 if (!Status(_("Creating transaction...")))
2218 if (nPrice + nTransactionFee > GetBalance())
2220 Error(_("Insufficient funds"));
2225 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2227 if (nPrice + nFeeRequired > GetBalance())
2228 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2230 Error(_("Transaction creation failed"));
2235 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2237 Error(_("Transaction aborted"));
2241 // Make sure we're still connected
2242 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2245 Error(_("Lost connection, transaction cancelled"));
2249 // Last chance to cancel
2261 if (!Status(_("Sending payment...")))
2265 if (!CommitTransaction(wtx, key))
2267 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."));
2271 // Send payment tx to seller, with response going to OnReply3 via event handler
2272 CWalletTx wtxSend = wtx;
2273 wtxSend.fFromMe = false;
2274 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2276 Status(_("Waiting for confirmation..."));
2281 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2283 ((CSendingDialog*)parg)->OnReply3(vRecv);
2286 void CSendingDialog::OnReply3(CDataStream& vRecv)
2294 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2295 "The transaction is recorded and will credit to the recipient,\n"
2296 "but the comment information will be blank."));
2302 //// what do we want to do about this?
2303 Error(_("Payment was sent, but an invalid response was received"));
2309 Status(_("Payment completed"));
2317 //////////////////////////////////////////////////////////////////////////////
2319 // CAddressBookDialog
2322 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2324 // Set initially selected page
2325 wxNotebookEvent event;
2326 event.SetSelection(nPageIn);
2327 OnNotebookPageChanged(event);
2328 m_notebook->ChangeSelection(nPageIn);
2330 fDuringSend = fDuringSendIn;
2332 m_buttonCancel->Show(false);
2335 wxIcon iconAddressBook;
2336 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2337 SetIcon(iconAddressBook);
2339 // Init column headers
2340 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2341 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2342 m_listCtrlSending->SetFocus();
2343 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2344 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2345 m_listCtrlReceiving->SetFocus();
2347 // Fill listctrl with address book data
2348 CRITICAL_BLOCK(cs_mapKeys)
2349 CRITICAL_BLOCK(cs_mapAddressBook)
2351 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2352 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2354 string strAddress = item.first;
2355 string strName = item.second;
2357 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2358 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2359 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2360 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2361 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2366 wxString CAddressBookDialog::GetSelectedAddress()
2368 int nIndex = GetSelection(m_listCtrl);
2371 return GetItemText(m_listCtrl, nIndex, 1);
2374 wxString CAddressBookDialog::GetSelectedSendingAddress()
2376 int nIndex = GetSelection(m_listCtrlSending);
2379 return GetItemText(m_listCtrlSending, nIndex, 1);
2382 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2384 int nIndex = GetSelection(m_listCtrlReceiving);
2387 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2390 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2393 nPage = event.GetSelection();
2394 if (nPage == SENDING)
2395 m_listCtrl = m_listCtrlSending;
2396 else if (nPage == RECEIVING)
2397 m_listCtrl = m_listCtrlReceiving;
2398 m_buttonDelete->Show(nPage == SENDING);
2399 m_buttonCopy->Show(nPage == RECEIVING);
2401 m_listCtrl->SetFocus();
2404 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2406 // Update address book with edited name
2408 if (event.IsEditCancelled())
2410 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2411 SetAddressBookName(strAddress, string(event.GetText()));
2412 pframeMain->RefreshListCtrl();
2415 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2418 if (nPage == RECEIVING)
2419 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2422 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2427 // Doubleclick returns selection
2428 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2432 // Doubleclick edits item
2433 wxCommandEvent event2;
2434 OnButtonEdit(event2);
2437 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2439 if (nPage != SENDING)
2441 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2443 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2445 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2446 CWalletDB().EraseName(strAddress);
2447 m_listCtrl->DeleteItem(nIndex);
2450 pframeMain->RefreshListCtrl();
2453 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2455 // Copy address box to clipboard
2456 if (wxTheClipboard->Open())
2458 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2459 wxTheClipboard->Close();
2463 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2466 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2468 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2472 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2474 int nIndex = GetSelection(m_listCtrl);
2477 string strName = (string)m_listCtrl->GetItemText(nIndex);
2478 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2479 string strAddressOrg = strAddress;
2481 if (nPage == SENDING)
2483 // Ask name and address
2486 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2487 if (!dialog.ShowModal())
2489 strName = dialog.GetValue1();
2490 strAddress = dialog.GetValue2();
2492 while (CheckIfMine(strAddress, _("Edit Address")));
2495 else if (nPage == RECEIVING)
2498 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2499 if (!dialog.ShowModal())
2501 strName = dialog.GetValue();
2505 if (strAddress != strAddressOrg)
2506 CWalletDB().EraseName(strAddressOrg);
2507 SetAddressBookName(strAddress, strName);
2508 m_listCtrl->SetItem(nIndex, 1, strAddress);
2509 m_listCtrl->SetItemText(nIndex, strName);
2510 pframeMain->RefreshListCtrl();
2513 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2518 if (nPage == SENDING)
2520 // Ask name and address
2523 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2524 if (!dialog.ShowModal())
2526 strName = dialog.GetValue1();
2527 strAddress = dialog.GetValue2();
2529 while (CheckIfMine(strAddress, _("Add Address")));
2531 else if (nPage == RECEIVING)
2534 CGetTextFromUserDialog dialog(this,
2535 _("New Receiving Address"),
2536 _("You should use a new address for each payment you receive.\n\nLabel"),
2538 if (!dialog.ShowModal())
2540 strName = dialog.GetValue();
2543 strAddress = PubKeyToAddress(GenerateNewKey());
2546 // Add to list and select it
2547 SetAddressBookName(strAddress, strName);
2548 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2549 SetSelection(m_listCtrl, nIndex);
2550 m_listCtrl->SetFocus();
2551 if (nPage == SENDING)
2552 pframeMain->RefreshListCtrl();
2555 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2558 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2561 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2567 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2578 //////////////////////////////////////////////////////////////////////////////
2585 ID_TASKBAR_RESTORE = 10001,
2587 ID_TASKBAR_GENERATE,
2591 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2592 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2593 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2594 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2595 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2596 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2597 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2600 void CMyTaskBarIcon::Show(bool fShow)
2602 static char pszPrevTip[200];
2605 string strTooltip = _("Bitcoin");
2606 if (fGenerateBitcoins)
2607 strTooltip = _("Bitcoin - Generating");
2608 if (fGenerateBitcoins && vNodes.empty())
2609 strTooltip = _("Bitcoin - (not connected)");
2611 // Optimization, only update when changed, using char array to be reentrant
2612 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2614 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2616 // somehow it'll choose the wrong size and scale it down if
2617 // we use the main icon, so we hand it one with only 16x16
2618 SetIcon(wxICON(favicon), strTooltip);
2620 SetIcon(bitcoin80_xpm, strTooltip);
2626 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2631 void CMyTaskBarIcon::Hide()
2636 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2641 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2646 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2648 // Since it's modal, get the main window to do it
2649 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2650 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2653 void CMyTaskBarIcon::Restore()
2656 wxIconizeEvent event(0, false);
2657 pframeMain->GetEventHandler()->AddPendingEvent(event);
2658 pframeMain->Iconize(false);
2659 pframeMain->Raise();
2662 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2664 GenerateBitcoins(event.IsChecked());
2667 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2669 event.Check(fGenerateBitcoins);
2672 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2674 pframeMain->Close(true);
2677 void CMyTaskBarIcon::UpdateTooltip()
2679 if (IsIconInstalled())
2683 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2685 wxMenu* pmenu = new wxMenu;
2686 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2687 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2688 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2689 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2690 pmenu->AppendSeparator();
2691 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2701 //////////////////////////////////////////////////////////////////////////////
2706 void CreateMainWindow()
2708 pframeMain = new CMainFrame(NULL);
2709 if (mapArgs.count("-min"))
2710 pframeMain->Iconize(true);
2711 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2712 if (!mapArgs.count("-minimizetotray"))
2713 fMinimizeToTray = false;
2715 pframeMain->Show(true); // have to show first to get taskbar button to hide
2716 if (fMinimizeToTray && pframeMain->IsIconized())
2717 fClosedToTray = true;
2718 pframeMain->Show(!fClosedToTray);
2719 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2720 CreateThread(ThreadDelayedRepaint, NULL);
2724 // Define a new application
2725 class CMyApp : public wxApp
2734 // Hook Initialize so we can start without GUI
2735 virtual bool Initialize(int& argc, wxChar** argv);
2737 // 2nd-level exception handling: we get all the exceptions occurring in any
2738 // event handler here
2739 virtual bool OnExceptionInMainLoop();
2741 // 3rd, and final, level exception handling: whenever an unhandled
2742 // exception is caught, this function is called
2743 virtual void OnUnhandledException();
2745 // and now for something different: this function is called in case of a
2746 // crash (e.g. dereferencing null pointer, division by 0, ...)
2747 virtual void OnFatalException();
2750 IMPLEMENT_APP(CMyApp)
2752 bool CMyApp::Initialize(int& argc, wxChar** argv)
2754 for (int i = 1; i < argc; i++)
2755 if (!IsSwitchChar(argv[i][0]))
2756 fCommandLine = true;
2760 // wxApp::Initialize will remove environment-specific parameters,
2761 // so it's too early to call ParseParameters yet
2762 for (int i = 1; i < argc; i++)
2764 wxString str = argv[i];
2766 if (str.size() >= 1 && str[0] == '/')
2768 char pszLower[MAX_PATH];
2769 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2773 if (str == "-daemon")
2779 if (fDaemon || fCommandLine)
2781 // Call the original Initialize while suppressing error messages
2782 // and ignoring failure. If unable to initialize GTK, it fails
2783 // near the end so hopefully the last few things don't matter.
2786 wxApp::Initialize(argc, argv);
2795 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2799 pthread_exit((void*)0);
2806 return wxApp::Initialize(argc, argv);
2809 bool CMyApp::OnInit()
2811 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2812 // Disable malfunctioning wxWidgets debug assertion
2813 extern int g_isPainting;
2814 g_isPainting = 10000;
2817 wxImage::AddHandler(new wxPNGHandler);
2819 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2820 SetAppName("Bitcoin");
2822 SetAppName("bitcoin");
2826 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2827 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2828 class wxMBConv_win32 : public wxMBConv
2832 size_t m_minMBCharWidth;
2834 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2835 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2839 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2840 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2841 g_locale.AddCatalogLookupPathPrefix("locale");
2843 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2844 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2846 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2847 g_locale.AddCatalog("bitcoin");
2849 return AppInit(argc, argv);
2852 int CMyApp::OnExit()
2855 return wxApp::OnExit();
2858 bool CMyApp::OnExceptionInMainLoop()
2864 catch (std::exception& e)
2866 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2867 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2873 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2874 wxLogWarning("Unknown exception");
2881 void CMyApp::OnUnhandledException()
2883 // this shows how we may let some exception propagate uncaught
2888 catch (std::exception& e)
2890 PrintException(&e, "CMyApp::OnUnhandledException()");
2891 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2897 PrintException(NULL, "CMyApp::OnUnhandledException()");
2898 wxLogWarning("Unknown exception");
2904 void CMyApp::OnFatalException()
2906 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);