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 && GetWarnings("statusbar") != "")
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 string strPrevWarning;
1017 string strWarning = GetWarnings("statusbar");
1018 if (strWarning != "")
1019 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1020 else if (strPrevWarning != "")
1021 m_statusBar->SetStatusText("", 0);
1022 strPrevWarning = strWarning;
1025 if (fGenerateBitcoins)
1026 strGen = _(" Generating");
1027 if (fGenerateBitcoins && vNodes.empty())
1028 strGen = _("(not connected)");
1029 m_statusBar->SetStatusText(strGen, 1);
1031 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1032 m_statusBar->SetStatusText(strStatus, 2);
1034 // Update receiving address
1035 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1036 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1037 m_textCtrlAddress->SetValue(strDefaultAddress);
1041 void UIThreadCall(boost::function0<void> fn)
1043 // Call this with a function object created with bind.
1044 // bind needs all parameters to match the function's expected types
1045 // and all default parameters specified. Some examples:
1046 // UIThreadCall(bind(wxBell));
1047 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1048 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1051 wxCommandEvent event(wxEVT_UITHREADCALL);
1052 event.SetClientData((void*)new boost::function0<void>(fn));
1053 pframeMain->GetEventHandler()->AddPendingEvent(event);
1057 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1059 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1064 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1070 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1072 // Options->Generate Coins
1073 GenerateBitcoins(event.IsChecked());
1076 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1078 event.Check(fGenerateBitcoins);
1081 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1083 // Options->Your Receiving Addresses
1084 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1085 if (!dialog.ShowModal())
1089 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1092 COptionsDialog dialog(this);
1096 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1099 CAboutDialog dialog(this);
1103 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1106 CSendDialog dialog(this);
1110 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1112 // Toolbar: Address Book
1113 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1114 if (dialogAddr.ShowModal() == 2)
1117 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1118 dialogSend.ShowModal();
1122 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1124 // Automatically select-all when entering window
1126 m_textCtrlAddress->SetSelection(-1, -1);
1127 fOnSetFocusAddress = true;
1130 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1133 if (fOnSetFocusAddress)
1134 m_textCtrlAddress->SetSelection(-1, -1);
1135 fOnSetFocusAddress = false;
1138 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1141 CGetTextFromUserDialog dialog(this,
1142 _("New Receiving Address"),
1143 _("You should use a new address for each payment you receive.\n\nLabel"),
1145 if (!dialog.ShowModal())
1147 string strName = dialog.GetValue();
1150 string strAddress = PubKeyToAddress(GenerateNewKey());
1153 SetAddressBookName(strAddress, strName);
1154 SetDefaultReceivingAddress(strAddress);
1157 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1159 // Copy address box to clipboard
1160 if (wxTheClipboard->Open())
1162 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1163 wxTheClipboard->Close();
1167 void CMainFrame::OnListItemActivated(wxListEvent& event)
1169 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1171 CRITICAL_BLOCK(cs_mapWallet)
1173 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1174 if (mi == mapWallet.end())
1176 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1181 CTxDetailsDialog dialog(this, wtx);
1183 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1192 //////////////////////////////////////////////////////////////////////////////
1197 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1199 CRITICAL_BLOCK(cs_mapAddressBook)
1202 strHTML.reserve(4000);
1203 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1205 int64 nTime = wtx.GetTxTime();
1206 int64 nCredit = wtx.GetCredit();
1207 int64 nDebit = wtx.GetDebit();
1208 int64 nNet = nCredit - nDebit;
1212 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1213 int nRequests = wtx.GetRequestCount();
1214 if (nRequests != -1)
1217 strHTML += _(", has not been successfully broadcast yet");
1218 else if (nRequests == 1)
1219 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1221 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1225 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1231 if (wtx.IsCoinBase())
1233 strHTML += _("<b>Source:</b> Generated<br>");
1235 else if (!wtx.mapValue["from"].empty())
1237 // Online transaction
1238 if (!wtx.mapValue["from"].empty())
1239 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1243 // Offline transaction
1247 foreach(const CTxOut& txout, wtx.vout)
1251 vector<unsigned char> vchPubKey;
1252 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1254 string strAddress = PubKeyToAddress(vchPubKey);
1255 if (mapAddressBook.count(strAddress))
1257 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1258 strHTML += _("<b>To:</b> ");
1259 strHTML += HtmlEscape(strAddress);
1260 if (!mapAddressBook[strAddress].empty())
1261 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1263 strHTML += _(" (yours)");
1278 if (!wtx.mapValue["to"].empty())
1280 // Online transaction
1281 strAddress = wtx.mapValue["to"];
1282 strHTML += _("<b>To:</b> ");
1283 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1284 strHTML += mapAddressBook[strAddress] + " ";
1285 strHTML += HtmlEscape(strAddress) + "<br>";
1292 if (wtx.IsCoinBase() && nCredit == 0)
1297 int64 nUnmatured = 0;
1298 foreach(const CTxOut& txout, wtx.vout)
1299 nUnmatured += txout.GetCredit();
1300 strHTML += _("<b>Credit:</b> ");
1301 if (wtx.IsInMainChain())
1302 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1304 strHTML += _("(not accepted)");
1312 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1316 bool fAllFromMe = true;
1317 foreach(const CTxIn& txin, wtx.vin)
1318 fAllFromMe = fAllFromMe && txin.IsMine();
1320 bool fAllToMe = true;
1321 foreach(const CTxOut& txout, wtx.vout)
1322 fAllToMe = fAllToMe && txout.IsMine();
1329 foreach(const CTxOut& txout, wtx.vout)
1334 if (wtx.mapValue["to"].empty())
1336 // Offline transaction
1338 if (ExtractHash160(txout.scriptPubKey, hash160))
1340 string strAddress = Hash160ToAddress(hash160);
1341 strHTML += _("<b>To:</b> ");
1342 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1343 strHTML += mapAddressBook[strAddress] + " ";
1344 strHTML += strAddress;
1349 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1355 /// issue: can't tell which is the payment and which is the change anymore
1356 //int64 nValue = wtx.vout[0].nValue;
1357 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1358 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1361 int64 nTxFee = nDebit - wtx.GetValueOut();
1363 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1368 // Mixed debit transaction
1370 foreach(const CTxIn& txin, wtx.vin)
1372 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1373 foreach(const CTxOut& txout, wtx.vout)
1375 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1379 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1385 if (!wtx.mapValue["message"].empty())
1386 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1388 if (wtx.IsCoinBase())
1389 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>";
1397 strHTML += "<hr><br>debug print<br><br>";
1398 foreach(const CTxIn& txin, wtx.vin)
1400 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1401 foreach(const CTxOut& txout, wtx.vout)
1403 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1405 strHTML += "<b>Inputs:</b><br>";
1406 CRITICAL_BLOCK(cs_mapWallet)
1408 foreach(const CTxIn& txin, wtx.vin)
1410 COutPoint prevout = txin.prevout;
1411 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1412 if (mi != mapWallet.end())
1414 const CWalletTx& prev = (*mi).second;
1415 if (prevout.n < prev.vout.size())
1417 strHTML += HtmlEscape(prev.ToString(), true);
1418 strHTML += " " + FormatTxStatus(prev) + ", ";
1419 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1425 strHTML += "<br><hr><br><b>Transaction:</b><br>";
1426 strHTML += HtmlEscape(wtx.ToString(), true);
1431 strHTML += "</font></html>";
1432 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1433 m_htmlWin->SetPage(strHTML);
1434 m_buttonOK->SetFocus();
1438 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1449 //////////////////////////////////////////////////////////////////////////////
1455 string StartupShortcutPath()
1457 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1460 bool GetStartOnSystemStartup()
1462 return filesystem::exists(StartupShortcutPath().c_str());
1465 void SetStartOnSystemStartup(bool fAutoStart)
1467 // If the shortcut exists already, remove it for updating
1468 remove(StartupShortcutPath().c_str());
1474 // Get a pointer to the IShellLink interface.
1475 IShellLink* psl = NULL;
1476 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1477 CLSCTX_INPROC_SERVER, IID_IShellLink,
1478 reinterpret_cast<void**>(&psl));
1480 if (SUCCEEDED(hres))
1482 // Get the current executable path
1483 TCHAR pszExePath[MAX_PATH];
1484 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1486 // Set the path to the shortcut target
1487 psl->SetPath(pszExePath);
1488 PathRemoveFileSpec(pszExePath);
1489 psl->SetWorkingDirectory(pszExePath);
1490 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1492 // Query IShellLink for the IPersistFile interface for
1493 // saving the shortcut in persistent storage.
1494 IPersistFile* ppf = NULL;
1495 hres = psl->QueryInterface(IID_IPersistFile,
1496 reinterpret_cast<void**>(&ppf));
1497 if (SUCCEEDED(hres))
1499 WCHAR pwsz[MAX_PATH];
1500 // Ensure that the string is ANSI.
1501 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1502 // Save the link by calling IPersistFile::Save.
1503 hres = ppf->Save(pwsz, TRUE);
1512 #elif defined(__WXGTK__)
1514 // Follow the Desktop Application Autostart Spec:
1515 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1517 boost::filesystem::path GetAutostartDir()
1519 namespace fs = boost::filesystem;
1521 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1522 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1523 char* pszHome = getenv("HOME");
1524 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1528 boost::filesystem::path GetAutostartFilePath()
1530 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1533 bool GetStartOnSystemStartup()
1535 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1536 if (!optionFile.good())
1538 // Scan through file for "Hidden=true":
1540 while (!optionFile.eof())
1542 getline(optionFile, line);
1543 if (line.find("Hidden") != string::npos &&
1544 line.find("true") != string::npos)
1552 void SetStartOnSystemStartup(bool fAutoStart)
1556 unlink(GetAutostartFilePath().native_file_string().c_str());
1560 char pszExePath[MAX_PATH+1];
1561 memset(pszExePath, 0, sizeof(pszExePath));
1562 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1565 boost::filesystem::create_directories(GetAutostartDir());
1567 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1568 if (!optionFile.good())
1570 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1573 // Write a bitcoin.desktop file to the autostart directory:
1574 optionFile << "[Desktop Entry]\n";
1575 optionFile << "Type=Application\n";
1576 optionFile << "Name=Bitcoin\n";
1577 optionFile << "Exec=" << pszExePath << "\n";
1578 optionFile << "Terminal=false\n";
1579 optionFile << "Hidden=false\n";
1585 // TODO: OSX startup stuff; see:
1586 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1588 bool GetStartOnSystemStartup() { return false; }
1589 void SetStartOnSystemStartup(bool fAutoStart) { }
1598 //////////////////////////////////////////////////////////////////////////////
1603 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1605 // Set up list box of page choices
1606 m_listBox->Append(_("Main"));
1607 //m_listBox->Append(_("Test 2"));
1608 m_listBox->SetSelection(0);
1610 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1611 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1612 if (!mapArgs.count("-minimizetotray"))
1614 // Minimize to tray is just too buggy on Linux
1615 fMinimizeToTray = false;
1616 m_checkBoxMinimizeToTray->SetValue(false);
1617 m_checkBoxMinimizeToTray->Enable(false);
1618 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1621 #ifdef __WXMAC_OSX__
1622 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1626 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1627 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1628 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1629 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1630 int nProcessors = wxThread::GetCPUCount();
1631 if (nProcessors < 1)
1633 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1634 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1635 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1636 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1637 m_checkBoxUseProxy->SetValue(fUseProxy);
1638 m_textCtrlProxyIP->Enable(fUseProxy);
1639 m_textCtrlProxyPort->Enable(fUseProxy);
1640 m_staticTextProxyIP->Enable(fUseProxy);
1641 m_staticTextProxyPort->Enable(fUseProxy);
1642 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1643 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1645 m_buttonOK->SetFocus();
1648 void COptionsDialog::SelectPage(int nPage)
1650 m_panelMain->Show(nPage == 0);
1651 m_panelTest2->Show(nPage == 1);
1653 m_scrolledWindow->Layout();
1654 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1657 void COptionsDialog::OnListBox(wxCommandEvent& event)
1659 SelectPage(event.GetSelection());
1662 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1665 int64 nTmp = nTransactionFee;
1666 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1667 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1670 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1672 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1675 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1677 m_textCtrlProxyIP->Enable(event.IsChecked());
1678 m_textCtrlProxyPort->Enable(event.IsChecked());
1679 m_staticTextProxyIP->Enable(event.IsChecked());
1680 m_staticTextProxyPort->Enable(event.IsChecked());
1683 CAddress COptionsDialog::GetProxyAddr()
1685 // Be careful about byte order, addr.ip and addr.port are big endian
1686 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1687 if (addr.ip == INADDR_NONE)
1688 addr.ip = addrProxy.ip;
1689 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1690 addr.port = htons(nPort);
1691 if (nPort <= 0 || nPort > USHRT_MAX)
1692 addr.port = addrProxy.port;
1696 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1699 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1700 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1704 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1706 OnButtonApply(event);
1710 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1715 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1719 int64 nPrevTransactionFee = nTransactionFee;
1720 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1721 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1723 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1724 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1726 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1727 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1729 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1731 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1732 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1734 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1735 GenerateBitcoins(fGenerateBitcoins);
1737 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1739 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1740 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1743 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1745 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1746 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1747 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1750 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1752 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1753 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1756 fUseProxy = m_checkBoxUseProxy->GetValue();
1757 walletdb.WriteSetting("fUseProxy", fUseProxy);
1759 addrProxy = GetProxyAddr();
1760 walletdb.WriteSetting("addrProxy", addrProxy);
1768 //////////////////////////////////////////////////////////////////////////////
1773 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1775 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer));
1777 // Change (c) into UTF-8 or ANSI copyright symbol
1778 wxString str = m_staticTextMain->GetLabel();
1780 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1782 str.Replace("(c)", "\xA9");
1784 m_staticTextMain->SetLabel(str);
1786 // Resize on Linux to make the window fit the text.
1787 // The text was wrapped manually rather than using the Wrap setting because
1788 // the wrap would be too small on Linux and it can't be changed at this point.
1789 wxFont fontTmp = m_staticTextMain->GetFont();
1790 if (fontTmp.GetPointSize() > 8);
1791 fontTmp.SetPointSize(8);
1792 m_staticTextMain->SetFont(fontTmp);
1793 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1797 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1807 //////////////////////////////////////////////////////////////////////////////
1812 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1815 m_textCtrlAddress->SetValue(strAddress);
1816 m_choiceTransferType->SetSelection(0);
1817 m_bitmapCheckMark->Show(false);
1818 fEnabledPrev = true;
1819 m_textCtrlAddress->SetFocus();
1820 //// todo: should add a display of your balance for convenience
1822 wxFont fontTmp = m_staticTextInstructions->GetFont();
1823 if (fontTmp.GetPointSize() > 9);
1824 fontTmp.SetPointSize(9);
1825 m_staticTextInstructions->SetFont(fontTmp);
1831 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1834 wxCommandEvent event;
1835 OnTextAddress(event);
1837 // Fixup the tab order
1838 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1839 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1843 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1847 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1848 m_bitmapCheckMark->Show(fBitcoinAddress);
1850 // Grey out message if bitcoin address
1851 bool fEnable = !fBitcoinAddress;
1852 m_staticTextFrom->Enable(fEnable);
1853 m_textCtrlFrom->Enable(fEnable);
1854 m_staticTextMessage->Enable(fEnable);
1855 m_textCtrlMessage->Enable(fEnable);
1856 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1857 if (!fEnable && fEnabledPrev)
1859 strFromSave = m_textCtrlFrom->GetValue();
1860 strMessageSave = m_textCtrlMessage->GetValue();
1861 m_textCtrlFrom->SetValue(_("n/a"));
1862 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1864 else if (fEnable && !fEnabledPrev)
1866 m_textCtrlFrom->SetValue(strFromSave);
1867 m_textCtrlMessage->SetValue(strMessageSave);
1869 fEnabledPrev = fEnable;
1872 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1874 // Reformat the amount
1876 if (m_textCtrlAmount->GetValue().Trim().empty())
1879 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1880 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1883 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1885 // Open address book
1886 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1887 if (dialog.ShowModal())
1888 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1891 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1893 // Copy clipboard to address box
1894 if (wxTheClipboard->Open())
1896 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1898 wxTextDataObject data;
1899 wxTheClipboard->GetData(data);
1900 m_textCtrlAddress->SetValue(data.GetText());
1902 wxTheClipboard->Close();
1906 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1909 string strAddress = (string)m_textCtrlAddress->GetValue();
1913 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1915 wxMessageBox(_("Error in amount "), _("Send Coins"));
1918 if (nValue > GetBalance())
1920 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1923 if (nValue + nTransactionFee > GetBalance())
1925 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1929 // Parse bitcoin address
1931 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1933 if (fBitcoinAddress)
1935 // Send to bitcoin address
1936 CScript scriptPubKey;
1937 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1939 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1941 wxMessageBox(_("Payment sent "), _("Sending..."));
1942 else if (strError != "ABORTED")
1943 wxMessageBox(strError + " ", _("Sending..."));
1948 CAddress addr(strAddress);
1949 if (!addr.IsValid())
1951 wxMessageBox(_("Invalid address "), _("Send Coins"));
1956 wtx.mapValue["to"] = strAddress;
1957 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1958 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1960 // Send to IP address
1961 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1962 if (!pdialog->ShowModal())
1966 CRITICAL_BLOCK(cs_mapAddressBook)
1967 if (!mapAddressBook.count(strAddress))
1968 SetAddressBookName(strAddress, "");
1973 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1984 //////////////////////////////////////////////////////////////////////////////
1989 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
1994 start = wxDateTime::UNow();
1995 memset(pszStatus, 0, sizeof(pszStatus));
2002 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2005 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2006 m_textCtrlStatus->SetValue("");
2008 CreateThread(SendingDialogStartTransfer, this);
2011 CSendingDialog::~CSendingDialog()
2013 printf("~CSendingDialog()\n");
2016 void CSendingDialog::Close()
2018 // Last one out turn out the lights.
2019 // fWorkDone signals that work side is done and UI thread should call destroy.
2020 // fUIDone signals that UI window has closed and work thread should call destroy.
2021 // This allows the window to disappear and end modality when cancelled
2022 // without making the user wait for ConnectNode to return. The dialog object
2023 // hangs around in the background until the work thread exits.
2034 void CSendingDialog::OnClose(wxCloseEvent& event)
2036 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2043 wxCommandEvent cmdevent;
2044 OnButtonCancel(cmdevent);
2048 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2054 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2060 void CSendingDialog::OnPaint(wxPaintEvent& event)
2063 if (strlen(pszStatus) > 130)
2064 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2066 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2067 m_staticTextSending->SetFocus();
2069 m_buttonCancel->Enable(false);
2072 m_buttonOK->Enable(true);
2073 m_buttonOK->SetFocus();
2074 m_buttonCancel->Enable(false);
2076 if (fAbort && fCanCancel && IsShown())
2078 strcpy(pszStatus, _("CANCELLED"));
2079 m_buttonOK->Enable(true);
2080 m_buttonOK->SetFocus();
2081 m_buttonCancel->Enable(false);
2082 m_buttonCancel->SetLabel(_("Cancelled"));
2084 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2090 // Everything from here on is not in the UI thread and must only communicate
2091 // with the rest of the dialog through variables and calling repaint.
2094 void CSendingDialog::Repaint()
2098 GetEventHandler()->AddPendingEvent(event);
2101 bool CSendingDialog::Status()
2108 if (fAbort && fCanCancel)
2110 memset(pszStatus, 0, 10);
2111 strcpy(pszStatus, _("CANCELLED"));
2119 bool CSendingDialog::Status(const string& str)
2124 // This can be read by the UI thread at any time,
2125 // so copy in a way that can be read cleanly at all times.
2126 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2127 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2133 bool CSendingDialog::Error(const string& str)
2137 Status(string(_("Error: ")) + str);
2141 void SendingDialogStartTransfer(void* parg)
2143 ((CSendingDialog*)parg)->StartTransfer();
2146 void CSendingDialog::StartTransfer()
2148 // Make sure we have enough money
2149 if (nPrice + nTransactionFee > GetBalance())
2151 Error(_("Insufficient funds"));
2155 // We may have connected already for product details
2156 if (!Status(_("Connecting...")))
2158 CNode* pnode = ConnectNode(addr, 15 * 60);
2161 Error(_("Unable to connect"));
2165 // Send order to seller, with response going to OnReply2 via event handler
2166 if (!Status(_("Requesting public key...")))
2168 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2171 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2173 ((CSendingDialog*)parg)->OnReply2(vRecv);
2176 void CSendingDialog::OnReply2(CDataStream& vRecv)
2178 if (!Status(_("Received public key...")))
2181 CScript scriptPubKey;
2189 vRecv >> strMessage;
2190 Error(_("Transfer was not accepted"));
2191 //// todo: enlarge the window and enable a hidden white box to put seller's message
2194 vRecv >> scriptPubKey;
2198 //// what do we want to do about this?
2199 Error(_("Invalid response received"));
2203 // Pause to give the user a chance to cancel
2204 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2211 CRITICAL_BLOCK(cs_main)
2214 if (!Status(_("Creating transaction...")))
2216 if (nPrice + nTransactionFee > GetBalance())
2218 Error(_("Insufficient funds"));
2223 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2225 if (nPrice + nFeeRequired > GetBalance())
2226 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2228 Error(_("Transaction creation failed"));
2233 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2235 Error(_("Transaction aborted"));
2239 // Make sure we're still connected
2240 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2243 Error(_("Lost connection, transaction cancelled"));
2247 // Last chance to cancel
2259 if (!Status(_("Sending payment...")))
2263 if (!CommitTransaction(wtx, key))
2265 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."));
2269 // Send payment tx to seller, with response going to OnReply3 via event handler
2270 CWalletTx wtxSend = wtx;
2271 wtxSend.fFromMe = false;
2272 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2274 Status(_("Waiting for confirmation..."));
2279 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2281 ((CSendingDialog*)parg)->OnReply3(vRecv);
2284 void CSendingDialog::OnReply3(CDataStream& vRecv)
2292 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2293 "The transaction is recorded and will credit to the recipient,\n"
2294 "but the comment information will be blank."));
2300 //// what do we want to do about this?
2301 Error(_("Payment was sent, but an invalid response was received"));
2307 Status(_("Payment completed"));
2315 //////////////////////////////////////////////////////////////////////////////
2317 // CAddressBookDialog
2320 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2322 // Set initially selected page
2323 wxNotebookEvent event;
2324 event.SetSelection(nPageIn);
2325 OnNotebookPageChanged(event);
2326 m_notebook->ChangeSelection(nPageIn);
2328 fDuringSend = fDuringSendIn;
2330 m_buttonCancel->Show(false);
2333 wxIcon iconAddressBook;
2334 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2335 SetIcon(iconAddressBook);
2337 // Init column headers
2338 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2339 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2340 m_listCtrlSending->SetFocus();
2341 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2342 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2343 m_listCtrlReceiving->SetFocus();
2345 // Fill listctrl with address book data
2346 CRITICAL_BLOCK(cs_mapKeys)
2347 CRITICAL_BLOCK(cs_mapAddressBook)
2349 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2350 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2352 string strAddress = item.first;
2353 string strName = item.second;
2355 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2356 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2357 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2358 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2359 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2364 wxString CAddressBookDialog::GetSelectedAddress()
2366 int nIndex = GetSelection(m_listCtrl);
2369 return GetItemText(m_listCtrl, nIndex, 1);
2372 wxString CAddressBookDialog::GetSelectedSendingAddress()
2374 int nIndex = GetSelection(m_listCtrlSending);
2377 return GetItemText(m_listCtrlSending, nIndex, 1);
2380 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2382 int nIndex = GetSelection(m_listCtrlReceiving);
2385 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2388 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2391 nPage = event.GetSelection();
2392 if (nPage == SENDING)
2393 m_listCtrl = m_listCtrlSending;
2394 else if (nPage == RECEIVING)
2395 m_listCtrl = m_listCtrlReceiving;
2396 m_buttonDelete->Show(nPage == SENDING);
2397 m_buttonCopy->Show(nPage == RECEIVING);
2399 m_listCtrl->SetFocus();
2402 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2404 // Update address book with edited name
2406 if (event.IsEditCancelled())
2408 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2409 SetAddressBookName(strAddress, string(event.GetText()));
2410 pframeMain->RefreshListCtrl();
2413 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2416 if (nPage == RECEIVING)
2417 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2420 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2425 // Doubleclick returns selection
2426 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2430 // Doubleclick edits item
2431 wxCommandEvent event2;
2432 OnButtonEdit(event2);
2435 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2437 if (nPage != SENDING)
2439 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2441 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2443 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2444 CWalletDB().EraseName(strAddress);
2445 m_listCtrl->DeleteItem(nIndex);
2448 pframeMain->RefreshListCtrl();
2451 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2453 // Copy address box to clipboard
2454 if (wxTheClipboard->Open())
2456 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2457 wxTheClipboard->Close();
2461 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2464 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2466 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2470 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2472 int nIndex = GetSelection(m_listCtrl);
2475 string strName = (string)m_listCtrl->GetItemText(nIndex);
2476 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2477 string strAddressOrg = strAddress;
2479 if (nPage == SENDING)
2481 // Ask name and address
2484 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2485 if (!dialog.ShowModal())
2487 strName = dialog.GetValue1();
2488 strAddress = dialog.GetValue2();
2490 while (CheckIfMine(strAddress, _("Edit Address")));
2493 else if (nPage == RECEIVING)
2496 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2497 if (!dialog.ShowModal())
2499 strName = dialog.GetValue();
2503 if (strAddress != strAddressOrg)
2504 CWalletDB().EraseName(strAddressOrg);
2505 SetAddressBookName(strAddress, strName);
2506 m_listCtrl->SetItem(nIndex, 1, strAddress);
2507 m_listCtrl->SetItemText(nIndex, strName);
2508 pframeMain->RefreshListCtrl();
2511 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2516 if (nPage == SENDING)
2518 // Ask name and address
2521 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2522 if (!dialog.ShowModal())
2524 strName = dialog.GetValue1();
2525 strAddress = dialog.GetValue2();
2527 while (CheckIfMine(strAddress, _("Add Address")));
2529 else if (nPage == RECEIVING)
2532 CGetTextFromUserDialog dialog(this,
2533 _("New Receiving Address"),
2534 _("You should use a new address for each payment you receive.\n\nLabel"),
2536 if (!dialog.ShowModal())
2538 strName = dialog.GetValue();
2541 strAddress = PubKeyToAddress(GenerateNewKey());
2544 // Add to list and select it
2545 SetAddressBookName(strAddress, strName);
2546 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2547 SetSelection(m_listCtrl, nIndex);
2548 m_listCtrl->SetFocus();
2549 if (nPage == SENDING)
2550 pframeMain->RefreshListCtrl();
2553 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2556 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2559 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2565 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2576 //////////////////////////////////////////////////////////////////////////////
2583 ID_TASKBAR_RESTORE = 10001,
2585 ID_TASKBAR_GENERATE,
2589 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2590 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2591 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2592 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2593 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2594 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2595 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2598 void CMyTaskBarIcon::Show(bool fShow)
2600 static char pszPrevTip[200];
2603 string strTooltip = _("Bitcoin");
2604 if (fGenerateBitcoins)
2605 strTooltip = _("Bitcoin - Generating");
2606 if (fGenerateBitcoins && vNodes.empty())
2607 strTooltip = _("Bitcoin - (not connected)");
2609 // Optimization, only update when changed, using char array to be reentrant
2610 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2612 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2614 // somehow it'll choose the wrong size and scale it down if
2615 // we use the main icon, so we hand it one with only 16x16
2616 SetIcon(wxICON(favicon), strTooltip);
2618 SetIcon(bitcoin80_xpm, strTooltip);
2624 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2629 void CMyTaskBarIcon::Hide()
2634 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2639 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2644 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2646 // Since it's modal, get the main window to do it
2647 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2648 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2651 void CMyTaskBarIcon::Restore()
2654 wxIconizeEvent event(0, false);
2655 pframeMain->GetEventHandler()->AddPendingEvent(event);
2656 pframeMain->Iconize(false);
2657 pframeMain->Raise();
2660 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2662 GenerateBitcoins(event.IsChecked());
2665 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2667 event.Check(fGenerateBitcoins);
2670 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2672 pframeMain->Close(true);
2675 void CMyTaskBarIcon::UpdateTooltip()
2677 if (IsIconInstalled())
2681 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2683 wxMenu* pmenu = new wxMenu;
2684 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2685 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2686 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2687 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2688 pmenu->AppendSeparator();
2689 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2699 //////////////////////////////////////////////////////////////////////////////
2704 void CreateMainWindow()
2706 pframeMain = new CMainFrame(NULL);
2707 if (mapArgs.count("-min"))
2708 pframeMain->Iconize(true);
2709 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2710 if (!mapArgs.count("-minimizetotray"))
2711 fMinimizeToTray = false;
2713 pframeMain->Show(true); // have to show first to get taskbar button to hide
2714 if (fMinimizeToTray && pframeMain->IsIconized())
2715 fClosedToTray = true;
2716 pframeMain->Show(!fClosedToTray);
2717 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2718 CreateThread(ThreadDelayedRepaint, NULL);
2722 // Define a new application
2723 class CMyApp : public wxApp
2732 // Hook Initialize so we can start without GUI
2733 virtual bool Initialize(int& argc, wxChar** argv);
2735 // 2nd-level exception handling: we get all the exceptions occurring in any
2736 // event handler here
2737 virtual bool OnExceptionInMainLoop();
2739 // 3rd, and final, level exception handling: whenever an unhandled
2740 // exception is caught, this function is called
2741 virtual void OnUnhandledException();
2743 // and now for something different: this function is called in case of a
2744 // crash (e.g. dereferencing null pointer, division by 0, ...)
2745 virtual void OnFatalException();
2748 IMPLEMENT_APP(CMyApp)
2750 bool CMyApp::Initialize(int& argc, wxChar** argv)
2752 for (int i = 1; i < argc; i++)
2753 if (!IsSwitchChar(argv[i][0]))
2754 fCommandLine = true;
2758 // wxApp::Initialize will remove environment-specific parameters,
2759 // so it's too early to call ParseParameters yet
2760 for (int i = 1; i < argc; i++)
2762 wxString str = argv[i];
2764 if (str.size() >= 1 && str[0] == '/')
2766 char pszLower[MAX_PATH];
2767 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2771 if (str == "-daemon")
2777 if (fDaemon || fCommandLine)
2779 // Call the original Initialize while suppressing error messages
2780 // and ignoring failure. If unable to initialize GTK, it fails
2781 // near the end so hopefully the last few things don't matter.
2784 wxApp::Initialize(argc, argv);
2793 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2797 pthread_exit((void*)0);
2804 return wxApp::Initialize(argc, argv);
2807 bool CMyApp::OnInit()
2809 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2810 // Disable malfunctioning wxWidgets debug assertion
2811 extern int g_isPainting;
2812 g_isPainting = 10000;
2815 wxImage::AddHandler(new wxPNGHandler);
2817 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2818 SetAppName("Bitcoin");
2820 SetAppName("bitcoin");
2824 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2825 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2826 class wxMBConv_win32 : public wxMBConv
2830 size_t m_minMBCharWidth;
2832 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2833 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2837 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2838 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2839 g_locale.AddCatalogLookupPathPrefix("locale");
2841 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2842 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2844 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2845 g_locale.AddCatalog("bitcoin");
2847 return AppInit(argc, argv);
2850 int CMyApp::OnExit()
2853 return wxApp::OnExit();
2856 bool CMyApp::OnExceptionInMainLoop()
2862 catch (std::exception& e)
2864 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2865 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2871 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2872 wxLogWarning("Unknown exception");
2879 void CMyApp::OnUnhandledException()
2881 // this shows how we may let some exception propagate uncaught
2886 catch (std::exception& e)
2888 PrintException(&e, "CMyApp::OnUnhandledException()");
2889 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2895 PrintException(NULL, "CMyApp::OnUnhandledException()");
2896 wxLogWarning("Unknown exception");
2902 void CMyApp::OnFatalException()
2904 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);