1 // Copyright (c) 2009-2010 Satoshi Nakamoto
\r
2 // Distributed under the MIT/X11 software license, see the accompanying
\r
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
\r
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
\r
14 CMainFrame* pframeMain = NULL;
\r
15 CMyTaskBarIcon* ptaskbaricon = NULL;
\r
16 bool fClosedToTray = false;
\r
27 //////////////////////////////////////////////////////////////////////////////
\r
32 void HandleCtrlA(wxKeyEvent& event)
\r
34 // Ctrl-a select all
\r
36 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
37 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
38 textCtrl->SetSelection(-1, -1);
\r
43 //char pszHourFormat[256];
\r
44 //pszHourFormat[0] = '\0';
\r
45 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
46 //return (pszHourFormat[0] != '0');
\r
50 string DateStr(int64 nTime)
\r
52 // Can only be used safely here in the UI
\r
53 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
56 string DateTimeStr(int64 nTime)
\r
58 // Can only be used safely here in the UI
\r
59 wxDateTime datetime((time_t)nTime);
\r
61 return (string)datetime.Format("%x %H:%M");
\r
63 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
66 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
68 // Helper to simplify access to listctrl
\r
70 item.m_itemId = nIndex;
\r
71 item.m_col = nColumn;
\r
72 item.m_mask = wxLIST_MASK_TEXT;
\r
73 if (!listCtrl->GetItem(item))
\r
75 return item.GetText();
\r
78 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
80 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
81 listCtrl->SetItem(nIndex, 1, str1);
\r
85 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
87 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
88 listCtrl->SetItem(nIndex, 1, str1);
\r
89 listCtrl->SetItem(nIndex, 2, str2);
\r
90 listCtrl->SetItem(nIndex, 3, str3);
\r
91 listCtrl->SetItem(nIndex, 4, str4);
\r
95 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
97 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
98 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
99 listCtrl->SetItem(nIndex, 1, str1);
\r
100 listCtrl->SetItem(nIndex, 2, str2);
\r
101 listCtrl->SetItem(nIndex, 3, str3);
\r
102 listCtrl->SetItem(nIndex, 4, str4);
\r
106 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
108 int nSize = listCtrl->GetItemCount();
\r
109 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
110 for (int i = 0; i < nSize; i++)
\r
111 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
114 int GetSelection(wxListCtrl* listCtrl)
\r
116 int nSize = listCtrl->GetItemCount();
\r
117 for (int i = 0; i < nSize; i++)
\r
118 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
123 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
126 for (const char* p = psz; *p; p++)
\r
128 if (*p == '<') len += 4;
\r
129 else if (*p == '>') len += 4;
\r
130 else if (*p == '&') len += 5;
\r
131 else if (*p == '"') len += 6;
\r
132 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
\r
133 else if (*p == '\n' && fMultiLine) len += 5;
\r
139 for (const char* p = psz; *p; p++)
\r
141 if (*p == '<') str += "<";
\r
142 else if (*p == '>') str += ">";
\r
143 else if (*p == '&') str += "&";
\r
144 else if (*p == '"') str += """;
\r
145 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
146 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
153 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
155 return HtmlEscape(str.c_str(), fMultiLine);
\r
158 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
160 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
164 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
167 return wxMessageBox(message, caption, style, parent, x, y);
\r
169 if (wxThread::IsMain() || fDaemon)
\r
171 return wxMessageBox(message, caption, style, parent, x, y);
\r
176 bool fDone = false;
\r
177 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
185 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
\r
187 if (nFeeRequired == 0 || fDaemon)
\r
189 string strMessage = strprintf(
\r
190 _("This transaction is over the size limit. You can still send it for a fee of %s, "
\r
191 "which goes to the nodes that process your transaction and helps to support the network. "
\r
192 "Do you want to pay the fee?"),
\r
193 FormatMoney(nFeeRequired).c_str());
\r
194 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
\r
197 void CalledSetStatusBar(const string& strText, int nField)
\r
199 if (nField == 0 && IsLockdown())
\r
201 if (pframeMain && pframeMain->m_statusBar)
\r
202 pframeMain->m_statusBar->SetStatusText(strText, nField);
\r
205 void SetDefaultReceivingAddress(const string& strAddress)
\r
207 // Update main window address and database
\r
208 if (pframeMain == NULL)
\r
210 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
\r
213 if (!AddressToHash160(strAddress, hash160))
\r
215 if (!mapPubKeys.count(hash160))
\r
217 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
218 pframeMain->m_textCtrlAddress->SetValue(strAddress);
\r
231 //////////////////////////////////////////////////////////////////////////////
\r
236 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
238 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
240 // Set initially selected page
\r
241 wxNotebookEvent event;
\r
242 event.SetSelection(0);
\r
243 OnNotebookPageChanged(event);
\r
244 m_notebook->ChangeSelection(0);
\r
247 fRefreshListCtrl = false;
\r
248 fRefreshListCtrlRunning = false;
\r
249 fOnSetFocusAddress = false;
\r
251 m_choiceFilter->SetSelection(0);
\r
252 double dResize = 1.0;
\r
254 SetIcon(wxICON(bitcoin));
\r
256 SetIcon(bitcoin80_xpm);
\r
257 SetBackgroundColour(m_toolBar->GetBackgroundColour());
\r
258 wxFont fontTmp = m_staticText41->GetFont();
\r
259 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
260 m_staticTextBalance->SetFont(fontTmp);
\r
261 m_staticTextBalance->SetSize(140, 17);
\r
262 // resize to fit ubuntu's huge default font
\r
264 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
\r
266 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
267 m_listCtrl->SetFocus();
\r
268 ptaskbaricon = new CMyTaskBarIcon();
\r
269 #ifdef __WXMAC_OSX__
\r
270 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
\r
271 // to their standard places, leaving these menus empty.
\r
272 GetMenuBar()->Remove(2); // remove Help menu
\r
273 GetMenuBar()->Remove(0); // remove File menu
\r
276 // Init column headers
\r
277 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
278 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
280 #ifdef __WXMAC_OSX__
\r
284 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
\r
285 foreach(wxListCtrl* p, pplistCtrl)
\r
287 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
288 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
289 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
\r
290 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
291 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
292 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
293 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
297 int pnWidths[3] = { -100, 88, 300 };
\r
299 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
300 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
302 m_statusBar->SetFieldsCount(3, pnWidths);
\r
304 // Fill your address text box
\r
305 vector<unsigned char> vchPubKey;
\r
306 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
307 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
309 // Fill listctrl with wallet transactions
\r
313 CMainFrame::~CMainFrame()
\r
316 delete ptaskbaricon;
\r
317 ptaskbaricon = NULL;
\r
320 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
\r
323 nPage = event.GetSelection();
\r
326 m_listCtrl = m_listCtrlAll;
\r
327 fShowGenerated = true;
\r
329 fShowReceived = true;
\r
331 else if (nPage == SENTRECEIVED)
\r
333 m_listCtrl = m_listCtrlSentReceived;
\r
334 fShowGenerated = false;
\r
336 fShowReceived = true;
\r
338 else if (nPage == SENT)
\r
340 m_listCtrl = m_listCtrlSent;
\r
341 fShowGenerated = false;
\r
343 fShowReceived = false;
\r
345 else if (nPage == RECEIVED)
\r
347 m_listCtrl = m_listCtrlReceived;
\r
348 fShowGenerated = false;
\r
350 fShowReceived = true;
\r
353 m_listCtrl->SetFocus();
\r
356 void CMainFrame::OnClose(wxCloseEvent& event)
\r
358 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
360 // Divert close to minimize
\r
362 fClosedToTray = true;
\r
368 CreateThread(Shutdown, NULL);
\r
372 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
375 // Hide the task bar button when minimized.
\r
376 // Event is sent when the frame is minimized or restored.
\r
377 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
378 // to get rid of the deprecated warning. Just ignore it.
\r
379 if (!event.Iconized())
\r
380 fClosedToTray = false;
\r
381 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
\r
382 if (mapArgs.count("-minimizetotray")) {
\r
384 // The tray icon sometimes disappears on ubuntu karmic
\r
385 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
\r
386 // Reports of CPU peg on 64-bit linux
\r
387 if (fMinimizeToTray && event.Iconized())
\r
388 fClosedToTray = true;
\r
389 Show(!fClosedToTray);
\r
390 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
391 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
\r
396 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
400 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
401 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
404 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
406 // Hidden columns not resizeable
\r
407 if (event.GetColumn() <= 1 && !fDebug)
\r
413 int CMainFrame::GetSortIndex(const string& strSort)
\r
418 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
419 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
420 // In the wx generic implementation, they store the list of items
\r
421 // in a vector, so indexed lookups are fast, but inserts are slower
\r
422 // the closer they are to the top.
\r
424 int high = m_listCtrl->GetItemCount();
\r
427 int mid = low + ((high - low) / 2);
\r
428 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
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)
\r
439 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
\r
440 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
\r
443 if (!fNew && nIndex == -1)
\r
445 string strHash = " " + hashKey.ToString();
\r
446 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
447 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
451 // fNew is for blind insert, only use if you're sure it's new
\r
452 if (fNew || nIndex == -1)
\r
454 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
458 // If sort key changed, must delete and reinsert to make it relocate
\r
459 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
\r
461 m_listCtrl->DeleteItem(nIndex);
\r
462 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
466 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
\r
467 m_listCtrl->SetItem(nIndex, 2, str2);
\r
468 m_listCtrl->SetItem(nIndex, 3, str3);
\r
469 m_listCtrl->SetItem(nIndex, 4, str4);
\r
470 m_listCtrl->SetItem(nIndex, 5, str5);
\r
471 m_listCtrl->SetItem(nIndex, 6, str6);
\r
472 m_listCtrl->SetItemData(nIndex, nData);
\r
475 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
477 long nData = *(long*)&hashKey;
\r
481 string strHash = " " + hashKey.ToString();
\r
482 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
483 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
487 m_listCtrl->DeleteItem(nIndex);
\r
489 return nIndex != -1;
\r
492 string FormatTxStatus(const CWalletTx& wtx)
\r
495 if (!wtx.IsFinal())
\r
497 if (wtx.nLockTime < 500000000)
\r
498 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
\r
500 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
\r
504 int nDepth = wtx.GetDepthInMainChain();
\r
505 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
506 return strprintf(_("%d/offline?"), nDepth);
\r
507 else if (nDepth < 6)
\r
508 return strprintf(_("%d/unconfirmed"), nDepth);
\r
510 return strprintf(_("%d confirmations"), nDepth);
\r
514 string SingleLine(const string& strIn)
\r
517 bool fOneSpace = false;
\r
518 foreach(int c, strIn)
\r
526 if (fOneSpace && !strOut.empty())
\r
535 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
537 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
538 int64 nCredit = wtx.GetCredit(true);
\r
539 int64 nDebit = wtx.GetDebit();
\r
540 int64 nNet = nCredit - nDebit;
\r
541 uint256 hash = wtx.GetHash();
\r
542 string strStatus = FormatTxStatus(wtx);
\r
543 map<string, string> mapValue = wtx.mapValue;
\r
544 wtx.nLinesDisplayed = 1;
\r
545 nListViewUpdated++;
\r
548 if (wtx.IsCoinBase())
\r
550 // Don't show generated coin until confirmed by at least one block after it
\r
551 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
553 // It is not an error when generated blocks are not accepted. By design,
\r
554 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
555 // This is the normal mechanism by which the network copes with latency.
\r
557 // We display regular transactions right away before any confirmation
\r
558 // because they can always get into some block eventually. Generated coins
\r
559 // are special because if their block is not accepted, they are not valid.
\r
561 if (wtx.GetDepthInMainChain() < 2)
\r
563 wtx.nLinesDisplayed = 0;
\r
567 if (!fShowGenerated)
\r
571 // Find the block the tx is in
\r
572 CBlockIndex* pindex = NULL;
\r
573 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
574 if (mi != mapBlockIndex.end())
\r
575 pindex = (*mi).second;
\r
577 // Sort order, unrecorded transactions sort to the top
\r
578 string strSort = strprintf("%010d-%01d-%010u",
\r
579 (pindex ? pindex->nHeight : INT_MAX),
\r
580 (wtx.IsCoinBase() ? 1 : 0),
\r
581 wtx.nTimeReceived);
\r
584 if (nNet > 0 || wtx.IsCoinBase())
\r
589 string strDescription;
\r
590 if (wtx.IsCoinBase())
\r
593 strDescription = _("Generated");
\r
596 int64 nUnmatured = 0;
\r
597 foreach(const CTxOut& txout, wtx.vout)
\r
598 nUnmatured += txout.GetCredit();
\r
599 if (wtx.IsInMainChain())
\r
601 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
603 // Check if the block was requested by anyone
\r
604 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
605 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
\r
609 strDescription = _("Generated (not accepted)");
\r
613 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
615 // Received by IP connection
\r
616 if (!fShowReceived)
\r
618 if (!mapValue["from"].empty())
\r
619 strDescription += _("From: ") + mapValue["from"];
\r
620 if (!mapValue["message"].empty())
\r
622 if (!strDescription.empty())
\r
623 strDescription += " - ";
\r
624 strDescription += mapValue["message"];
\r
629 // Received by Bitcoin Address
\r
630 if (!fShowReceived)
\r
632 foreach(const CTxOut& txout, wtx.vout)
\r
634 if (txout.IsMine())
\r
636 vector<unsigned char> vchPubKey;
\r
637 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
639 CRITICAL_BLOCK(cs_mapAddressBook)
\r
641 //strDescription += _("Received payment to ");
\r
642 //strDescription += _("Received with address ");
\r
643 strDescription += _("From: unknown, Received with: ");
\r
644 string strAddress = PubKeyToAddress(vchPubKey);
\r
645 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
\r
646 if (mi != mapAddressBook.end() && !(*mi).second.empty())
\r
648 string strLabel = (*mi).second;
\r
649 strDescription += strAddress.substr(0,12) + "... ";
\r
650 strDescription += "(" + strLabel + ")";
\r
653 strDescription += strAddress;
\r
661 InsertLine(fNew, nIndex, hash, strSort,
\r
663 nTime ? DateTimeStr(nTime) : "",
\r
664 SingleLine(strDescription),
\r
666 FormatMoney(nNet, true));
\r
670 bool fAllFromMe = true;
\r
671 foreach(const CTxIn& txin, wtx.vin)
\r
672 fAllFromMe = fAllFromMe && txin.IsMine();
\r
674 bool fAllToMe = true;
\r
675 foreach(const CTxOut& txout, wtx.vout)
\r
676 fAllToMe = fAllToMe && txout.IsMine();
\r
678 if (fAllFromMe && fAllToMe)
\r
681 int64 nValue = wtx.vout[0].nValue;
\r
682 InsertLine(fNew, nIndex, hash, strSort,
\r
684 nTime ? DateTimeStr(nTime) : "",
\r
685 _("Payment to yourself"),
\r
688 /// issue: can't tell which is the payment and which is the change anymore
\r
689 // FormatMoney(nNet - nValue, true),
\r
690 // FormatMoney(nValue, true));
\r
692 else if (fAllFromMe)
\r
700 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
701 wtx.nLinesDisplayed = 0;
\r
702 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
704 const CTxOut& txout = wtx.vout[nOut];
\r
705 if (txout.IsMine())
\r
709 if (!mapValue["to"].empty())
\r
712 strAddress = mapValue["to"];
\r
716 // Sent to Bitcoin Address
\r
718 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
719 strAddress = Hash160ToAddress(hash160);
\r
722 string strDescription = _("To: ");
\r
723 CRITICAL_BLOCK(cs_mapAddressBook)
\r
724 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
725 strDescription += mapAddressBook[strAddress] + " ";
\r
726 strDescription += strAddress;
\r
727 if (!mapValue["message"].empty())
\r
729 if (!strDescription.empty())
\r
730 strDescription += " - ";
\r
731 strDescription += mapValue["message"];
\r
734 int64 nValue = txout.nValue;
\r
741 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
743 nTime ? DateTimeStr(nTime) : "",
\r
744 SingleLine(strDescription),
\r
745 FormatMoney(-nValue, true),
\r
747 wtx.nLinesDisplayed++;
\r
753 // Mixed debit transaction, can't break down payees
\r
755 bool fAllMine = true;
\r
756 foreach(const CTxOut& txout, wtx.vout)
\r
757 fAllMine = fAllMine && txout.IsMine();
\r
758 foreach(const CTxIn& txin, wtx.vin)
\r
759 fAllMine = fAllMine && txin.IsMine();
\r
761 InsertLine(fNew, nIndex, hash, strSort,
\r
763 nTime ? DateTimeStr(nTime) : "",
\r
765 FormatMoney(nNet, true),
\r
773 void CMainFrame::RefreshListCtrl()
\r
775 fRefreshListCtrl = true;
\r
779 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
781 if (fRefreshListCtrl)
\r
783 // Collect list of wallet transactions and sort newest first
\r
784 bool fEntered = false;
\r
785 vector<pair<unsigned int, uint256> > vSorted;
\r
786 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
788 printf("RefreshListCtrl starting\n");
\r
790 fRefreshListCtrl = false;
\r
791 vWalletUpdated.clear();
\r
793 // Do the newest transactions first
\r
794 vSorted.reserve(mapWallet.size());
\r
795 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
797 const CWalletTx& wtx = (*it).second;
\r
798 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
799 vSorted.push_back(make_pair(nTime, (*it).first));
\r
801 m_listCtrl->DeleteAllItems();
\r
806 sort(vSorted.begin(), vSorted.end());
\r
808 // Fill list control
\r
809 for (int i = 0; i < vSorted.size();)
\r
813 bool fEntered = false;
\r
814 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
817 uint256& hash = vSorted[i++].second;
\r
818 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
819 if (mi != mapWallet.end())
\r
820 InsertTransaction((*mi).second, true);
\r
822 if (!fEntered || i == 100 || i % 500 == 0)
\r
826 printf("RefreshListCtrl done\n");
\r
828 // Update transaction total display
\r
829 MainFrameRepaint();
\r
833 // Check for time updates
\r
834 static int64 nLastTime;
\r
835 if (GetTime() > nLastTime + 30)
\r
837 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
839 nLastTime = GetTime();
\r
840 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
842 CWalletTx& wtx = (*it).second;
\r
843 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
844 InsertTransaction(wtx, false);
\r
851 void CMainFrame::RefreshStatusColumn()
\r
853 static int nLastTop;
\r
854 static CBlockIndex* pindexLastBest;
\r
855 static unsigned int nLastRefreshed;
\r
857 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
858 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
861 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
864 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
866 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
868 // If no updates, only need to do the part that moved onto the screen
\r
869 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
870 nStart = nLastTop + 100;
\r
871 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
875 pindexLastBest = pindexBest;
\r
876 nLastRefreshed = nListViewUpdated;
\r
878 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
880 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
881 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
882 if (mi == mapWallet.end())
\r
884 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
887 CWalletTx& wtx = (*mi).second;
\r
888 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
890 if (!InsertTransaction(wtx, false, nIndex))
\r
891 m_listCtrl->DeleteItem(nIndex--);
\r
894 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
899 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
910 unsigned int nNeedRepaint = 0;
\r
911 unsigned int nLastRepaint = 0;
\r
912 int64 nLastRepaintTime = 0;
\r
913 int64 nRepaintInterval = 500;
\r
915 void ThreadDelayedRepaint(void* parg)
\r
919 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
921 nLastRepaint = nNeedRepaint;
\r
924 printf("DelayedRepaint\n");
\r
925 wxPaintEvent event;
\r
926 pframeMain->fRefresh = true;
\r
927 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
930 Sleep(nRepaintInterval);
\r
934 void MainFrameRepaint()
\r
936 // This is called by network code that shouldn't access pframeMain
\r
937 // directly because it could still be running after the UI is closed.
\r
940 // Don't repaint too often
\r
941 static int64 nLastRepaintRequest;
\r
942 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
947 nLastRepaintRequest = GetTimeMillis();
\r
949 printf("MainFrameRepaint\n");
\r
950 wxPaintEvent event;
\r
951 pframeMain->fRefresh = true;
\r
952 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
956 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
958 // Skip lets the listctrl do the paint, we're just hooking the message
\r
962 ptaskbaricon->UpdateTooltip();
\r
967 static int nTransactionCount;
\r
968 bool fPaintedBalance = false;
\r
969 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
971 nLastRepaint = nNeedRepaint;
\r
972 nLastRepaintTime = GetTimeMillis();
\r
974 // Update listctrl contents
\r
975 if (!vWalletUpdated.empty())
\r
977 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
980 if (m_listCtrl->GetItemCount())
\r
981 strTop = (string)m_listCtrl->GetItemText(0);
\r
982 foreach(uint256 hash, vWalletUpdated)
\r
984 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
985 if (mi != mapWallet.end())
\r
986 InsertTransaction((*mi).second, false);
\r
988 vWalletUpdated.clear();
\r
989 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
990 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
995 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
997 fPaintedBalance = true;
\r
998 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
1000 // Count hidden and multi-line transactions
\r
1001 nTransactionCount = 0;
\r
1002 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
1004 CWalletTx& wtx = (*it).second;
\r
1005 nTransactionCount += wtx.nLinesDisplayed;
\r
1009 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1012 // Update status column of visible items only
\r
1013 RefreshStatusColumn();
\r
1015 // Update status bar
\r
1016 static bool fPrevLockdown;
\r
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);
\r
1019 else if (fPrevLockdown)
\r
1020 m_statusBar->SetStatusText("", 0);
\r
1021 fPrevLockdown = IsLockdown();
\r
1023 string strGen = "";
\r
1024 if (fGenerateBitcoins)
\r
1025 strGen = _(" Generating");
\r
1026 if (fGenerateBitcoins && vNodes.empty())
\r
1027 strGen = _("(not connected)");
\r
1028 m_statusBar->SetStatusText(strGen, 1);
\r
1030 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1031 m_statusBar->SetStatusText(strStatus, 2);
\r
1033 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1034 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1036 // Update receiving address
\r
1037 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
\r
1038 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
\r
1039 m_textCtrlAddress->SetValue(strDefaultAddress);
\r
1043 void UIThreadCall(boost::function0<void> fn)
\r
1045 // Call this with a function object created with bind.
\r
1046 // bind needs all parameters to match the function's expected types
\r
1047 // and all default parameters specified. Some examples:
\r
1048 // UIThreadCall(bind(wxBell));
\r
1049 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1050 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1053 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1054 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1055 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1059 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1061 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1066 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1072 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1074 // Options->Generate Coins
\r
1075 GenerateBitcoins(event.IsChecked());
\r
1078 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1080 event.Check(fGenerateBitcoins);
\r
1083 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1085 // Options->Your Receiving Addresses
\r
1086 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
\r
1087 if (!dialog.ShowModal())
\r
1091 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1093 // Options->Options
\r
1094 COptionsDialog dialog(this);
\r
1095 dialog.ShowModal();
\r
1098 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1101 CAboutDialog dialog(this);
\r
1102 dialog.ShowModal();
\r
1105 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1108 CSendDialog dialog(this);
\r
1109 dialog.ShowModal();
\r
1112 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1114 // Toolbar: Address Book
\r
1115 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
\r
1116 if (dialogAddr.ShowModal() == 2)
\r
1119 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
\r
1120 dialogSend.ShowModal();
\r
1124 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1126 // Automatically select-all when entering window
\r
1128 m_textCtrlAddress->SetSelection(-1, -1);
\r
1129 fOnSetFocusAddress = true;
\r
1132 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1135 if (fOnSetFocusAddress)
\r
1136 m_textCtrlAddress->SetSelection(-1, -1);
\r
1137 fOnSetFocusAddress = false;
\r
1140 void CMainFrame::OnButtonNew(wxCommandEvent& event)
\r
1143 CGetTextFromUserDialog dialog(this,
\r
1144 _("New Receiving Address"),
\r
1145 _("You should use a new address for each payment you receive.\n\nLabel"),
\r
1147 if (!dialog.ShowModal())
\r
1149 string strName = dialog.GetValue();
\r
1151 // Generate new key
\r
1152 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
1155 SetAddressBookName(strAddress, strName);
\r
1156 SetDefaultReceivingAddress(strAddress);
\r
1159 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1161 // Copy address box to clipboard
\r
1162 if (wxTheClipboard->Open())
\r
1164 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1165 wxTheClipboard->Close();
\r
1169 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1171 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1173 CRITICAL_BLOCK(cs_mapWallet)
\r
1175 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1176 if (mi == mapWallet.end())
\r
1178 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1181 wtx = (*mi).second;
\r
1183 CTxDetailsDialog dialog(this, wtx);
\r
1184 dialog.ShowModal();
\r
1185 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1186 //pdialog->Show();
\r
1194 //////////////////////////////////////////////////////////////////////////////
\r
1196 // CTxDetailsDialog
\r
1199 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1201 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1204 strHTML.reserve(4000);
\r
1205 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1207 int64 nTime = wtx.GetTxTime();
\r
1208 int64 nCredit = wtx.GetCredit();
\r
1209 int64 nDebit = wtx.GetDebit();
\r
1210 int64 nNet = nCredit - nDebit;
\r
1214 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
\r
1215 int nRequests = wtx.GetRequestCount();
\r
1216 if (nRequests != -1)
\r
1218 if (nRequests == 0)
\r
1219 strHTML += _(", has not been successfully broadcast yet");
\r
1220 else if (nRequests == 1)
\r
1221 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
\r
1223 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
\r
1225 strHTML += "<br>";
\r
1227 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1233 if (wtx.IsCoinBase())
\r
1235 strHTML += _("<b>Source:</b> Generated<br>");
\r
1237 else if (!wtx.mapValue["from"].empty())
\r
1239 // Online transaction
\r
1240 if (!wtx.mapValue["from"].empty())
\r
1241 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1245 // Offline transaction
\r
1249 foreach(const CTxOut& txout, wtx.vout)
\r
1251 if (txout.IsMine())
\r
1253 vector<unsigned char> vchPubKey;
\r
1254 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1256 string strAddress = PubKeyToAddress(vchPubKey);
\r
1257 if (mapAddressBook.count(strAddress))
\r
1259 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
\r
1260 strHTML += _("<b>To:</b> ");
\r
1261 strHTML += HtmlEscape(strAddress);
\r
1262 if (!mapAddressBook[strAddress].empty())
\r
1263 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
\r
1265 strHTML += _(" (yours)");
\r
1266 strHTML += "<br>";
\r
1279 string strAddress;
\r
1280 if (!wtx.mapValue["to"].empty())
\r
1282 // Online transaction
\r
1283 strAddress = wtx.mapValue["to"];
\r
1284 strHTML += _("<b>To:</b> ");
\r
1285 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1286 strHTML += mapAddressBook[strAddress] + " ";
\r
1287 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1294 if (wtx.IsCoinBase() && nCredit == 0)
\r
1299 int64 nUnmatured = 0;
\r
1300 foreach(const CTxOut& txout, wtx.vout)
\r
1301 nUnmatured += txout.GetCredit();
\r
1302 strHTML += _("<b>Credit:</b> ");
\r
1303 if (wtx.IsInMainChain())
\r
1304 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1306 strHTML += _("(not accepted)");
\r
1307 strHTML += "<br>";
\r
1309 else if (nNet > 0)
\r
1314 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
\r
1318 bool fAllFromMe = true;
\r
1319 foreach(const CTxIn& txin, wtx.vin)
\r
1320 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1322 bool fAllToMe = true;
\r
1323 foreach(const CTxOut& txout, wtx.vout)
\r
1324 fAllToMe = fAllToMe && txout.IsMine();
\r
1331 foreach(const CTxOut& txout, wtx.vout)
\r
1333 if (txout.IsMine())
\r
1336 if (wtx.mapValue["to"].empty())
\r
1338 // Offline transaction
\r
1340 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1342 string strAddress = Hash160ToAddress(hash160);
\r
1343 strHTML += _("<b>To:</b> ");
\r
1344 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1345 strHTML += mapAddressBook[strAddress] + " ";
\r
1346 strHTML += strAddress;
\r
1347 strHTML += "<br>";
\r
1351 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
\r
1356 // Payment to self
\r
1357 /// issue: can't tell which is the payment and which is the change anymore
\r
1358 //int64 nValue = wtx.vout[0].nValue;
\r
1359 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
\r
1360 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
\r
1363 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1365 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
\r
1370 // Mixed debit transaction
\r
1372 foreach(const CTxIn& txin, wtx.vin)
\r
1373 if (txin.IsMine())
\r
1374 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1375 foreach(const CTxOut& txout, wtx.vout)
\r
1376 if (txout.IsMine())
\r
1377 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
\r
1381 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
\r
1387 if (!wtx.mapValue["message"].empty())
\r
1388 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1390 if (wtx.IsCoinBase())
\r
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>";
\r
1399 strHTML += "<hr><br>debug print<br><br>";
\r
1400 foreach(const CTxIn& txin, wtx.vin)
\r
1401 if (txin.IsMine())
\r
1402 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1403 foreach(const CTxOut& txout, wtx.vout)
\r
1404 if (txout.IsMine())
\r
1405 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1407 strHTML += "<b>Inputs:</b><br>";
\r
1408 CRITICAL_BLOCK(cs_mapWallet)
\r
1410 foreach(const CTxIn& txin, wtx.vin)
\r
1412 COutPoint prevout = txin.prevout;
\r
1413 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1414 if (mi != mapWallet.end())
\r
1416 const CWalletTx& prev = (*mi).second;
\r
1417 if (prevout.n < prev.vout.size())
\r
1419 strHTML += HtmlEscape(prev.ToString(), true);
\r
1420 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1421 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1427 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1428 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1433 strHTML += "</font></html>";
\r
1434 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1435 m_htmlWin->SetPage(strHTML);
\r
1436 m_buttonOK->SetFocus();
\r
1440 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1451 //////////////////////////////////////////////////////////////////////////////
\r
1457 string StartupShortcutPath()
\r
1459 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
1462 bool GetStartOnSystemStartup()
\r
1464 return filesystem::exists(StartupShortcutPath().c_str());
\r
1467 void SetStartOnSystemStartup(bool fAutoStart)
\r
1469 // If the shortcut exists already, remove it for updating
\r
1470 remove(StartupShortcutPath().c_str());
\r
1474 CoInitialize(NULL);
\r
1476 // Get a pointer to the IShellLink interface.
\r
1477 IShellLink* psl = NULL;
\r
1478 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
1479 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
1480 reinterpret_cast<void**>(&psl));
\r
1482 if (SUCCEEDED(hres))
\r
1484 // Get the current executable path
\r
1485 TCHAR pszExePath[MAX_PATH];
\r
1486 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
1488 // Set the path to the shortcut target
\r
1489 psl->SetPath(pszExePath);
\r
1490 PathRemoveFileSpec(pszExePath);
\r
1491 psl->SetWorkingDirectory(pszExePath);
\r
1492 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
1494 // Query IShellLink for the IPersistFile interface for
\r
1495 // saving the shortcut in persistent storage.
\r
1496 IPersistFile* ppf = NULL;
\r
1497 hres = psl->QueryInterface(IID_IPersistFile,
\r
1498 reinterpret_cast<void**>(&ppf));
\r
1499 if (SUCCEEDED(hres))
\r
1501 WCHAR pwsz[MAX_PATH];
\r
1502 // Ensure that the string is ANSI.
\r
1503 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
1504 // Save the link by calling IPersistFile::Save.
\r
1505 hres = ppf->Save(pwsz, TRUE);
\r
1514 #elif defined(__WXGTK__)
\r
1516 // Follow the Desktop Application Autostart Spec:
\r
1517 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
\r
1519 boost::filesystem::path GetAutostartDir()
\r
1521 namespace fs = boost::filesystem;
\r
1523 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
\r
1524 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
\r
1525 char* pszHome = getenv("HOME");
\r
1526 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
\r
1527 return fs::path();
\r
1530 boost::filesystem::path GetAutostartFilePath()
\r
1532 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
\r
1535 bool GetStartOnSystemStartup()
\r
1537 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
\r
1538 if (!optionFile.good())
\r
1540 // Scan through file for "Hidden=true":
\r
1542 while (!optionFile.eof())
\r
1544 getline(optionFile, line);
\r
1545 if (line.find("Hidden") != string::npos &&
\r
1546 line.find("true") != string::npos)
\r
1549 optionFile.close();
\r
1554 void SetStartOnSystemStartup(bool fAutoStart)
\r
1558 unlink(GetAutostartFilePath().native_file_string().c_str());
\r
1562 boost::filesystem::create_directories(GetAutostartDir());
\r
1564 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
\r
1565 if (!optionFile.good())
\r
1567 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
\r
1570 // Write a bitcoin.desktop file to the autostart directory:
\r
1571 char pszExePath[MAX_PATH+1];
\r
1572 memset(pszExePath, 0, sizeof(pszExePath));
\r
1573 readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1);
\r
1574 optionFile << "[Desktop Entry]\n";
\r
1575 optionFile << "Type=Application\n";
\r
1576 optionFile << "Name=Bitcoin\n";
\r
1577 optionFile << "Exec=" << pszExePath << "\n";
\r
1578 optionFile << "Terminal=false\n";
\r
1579 optionFile << "Hidden=false\n";
\r
1580 optionFile.close();
\r
1585 // TODO: OSX startup stuff; see:
\r
1586 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
\r
1588 bool GetStartOnSystemStartup() { return false; }
\r
1589 void SetStartOnSystemStartup(bool fAutoStart) { }
\r
1598 //////////////////////////////////////////////////////////////////////////////
\r
1603 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1605 // Set up list box of page choices
\r
1606 m_listBox->Append(_("Main"));
\r
1607 //m_listBox->Append(_("Test 2"));
\r
1608 m_listBox->SetSelection(0);
\r
1610 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
\r
1611 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
\r
1612 if (!mapArgs.count("-minimizetotray"))
\r
1614 // Minimize to tray is just too buggy on Linux
\r
1615 fMinimizeToTray = false;
\r
1616 m_checkBoxMinimizeToTray->SetValue(false);
\r
1617 m_checkBoxMinimizeToTray->Enable(false);
\r
1618 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
\r
1621 #ifdef __WXMAC_OSX__
\r
1622 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1626 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1627 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1628 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1629 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1630 int nProcessors = wxThread::GetCPUCount();
\r
1631 if (nProcessors < 1)
\r
1632 nProcessors = 999;
\r
1633 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1634 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1635 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1636 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1637 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1638 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1639 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1640 m_staticTextProxyIP->Enable(fUseProxy);
\r
1641 m_staticTextProxyPort->Enable(fUseProxy);
\r
1642 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1643 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1645 m_buttonOK->SetFocus();
\r
1648 void COptionsDialog::SelectPage(int nPage)
\r
1650 m_panelMain->Show(nPage == 0);
\r
1651 m_panelTest2->Show(nPage == 1);
\r
1653 m_scrolledWindow->Layout();
\r
1654 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1657 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1659 SelectPage(event.GetSelection());
\r
1662 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1665 int64 nTmp = nTransactionFee;
\r
1666 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1667 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1670 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1672 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1675 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1677 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1678 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1679 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1680 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1683 CAddress COptionsDialog::GetProxyAddr()
\r
1685 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1686 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1687 if (addr.ip == INADDR_NONE)
\r
1688 addr.ip = addrProxy.ip;
\r
1689 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1690 addr.port = htons(nPort);
\r
1691 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1692 addr.port = addrProxy.port;
\r
1696 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1699 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1700 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1704 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1706 OnButtonApply(event);
\r
1710 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1715 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1717 CWalletDB walletdb;
\r
1719 int64 nPrevTransactionFee = nTransactionFee;
\r
1720 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1721 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1723 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1724 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1726 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1727 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1729 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1731 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1732 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1734 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1735 GenerateBitcoins(fGenerateBitcoins);
\r
1737 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1739 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1740 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1743 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1745 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1746 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1747 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1750 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1752 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1753 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1756 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1757 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1759 addrProxy = GetProxyAddr();
\r
1760 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1768 //////////////////////////////////////////////////////////////////////////////
\r
1773 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1775 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer));
\r
1777 // Change (c) into UTF-8 or ANSI copyright symbol
\r
1778 wxString str = m_staticTextMain->GetLabel();
\r
1780 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
\r
1782 str.Replace("(c)", "\xA9");
\r
1784 m_staticTextMain->SetLabel(str);
\r
1786 // Resize on Linux to make the window fit the text.
\r
1787 // The text was wrapped manually rather than using the Wrap setting because
\r
1788 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1789 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1790 if (fontTmp.GetPointSize() > 8);
\r
1791 fontTmp.SetPointSize(8);
\r
1792 m_staticTextMain->SetFont(fontTmp);
\r
1793 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
\r
1797 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1807 //////////////////////////////////////////////////////////////////////////////
\r
1812 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1815 m_textCtrlAddress->SetValue(strAddress);
\r
1816 m_choiceTransferType->SetSelection(0);
\r
1817 m_bitmapCheckMark->Show(false);
\r
1818 fEnabledPrev = true;
\r
1819 m_textCtrlAddress->SetFocus();
\r
1820 //// todo: should add a display of your balance for convenience
\r
1822 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1823 if (fontTmp.GetPointSize() > 9);
\r
1824 fontTmp.SetPointSize(9);
\r
1825 m_staticTextInstructions->SetFont(fontTmp);
\r
1826 SetSize(725, 380);
\r
1831 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1832 SetIcon(iconSend);
\r
1834 wxCommandEvent event;
\r
1835 OnTextAddress(event);
\r
1837 // Fixup the tab order
\r
1838 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1839 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1843 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1847 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1848 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1850 // Grey out message if bitcoin address
\r
1851 bool fEnable = !fBitcoinAddress;
\r
1852 m_staticTextFrom->Enable(fEnable);
\r
1853 m_textCtrlFrom->Enable(fEnable);
\r
1854 m_staticTextMessage->Enable(fEnable);
\r
1855 m_textCtrlMessage->Enable(fEnable);
\r
1856 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1857 if (!fEnable && fEnabledPrev)
\r
1859 strFromSave = m_textCtrlFrom->GetValue();
\r
1860 strMessageSave = m_textCtrlMessage->GetValue();
\r
1861 m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
\r
1862 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
\r
1864 else if (fEnable && !fEnabledPrev)
\r
1866 m_textCtrlFrom->SetValue(strFromSave);
\r
1867 m_textCtrlMessage->SetValue(strMessageSave);
\r
1869 fEnabledPrev = fEnable;
\r
1872 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1874 // Reformat the amount
\r
1876 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1879 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1880 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1883 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1885 // Open address book
\r
1886 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
\r
1887 if (dialog.ShowModal())
\r
1888 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
\r
1891 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1893 // Copy clipboard to address box
\r
1894 if (wxTheClipboard->Open())
\r
1896 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1898 wxTextDataObject data;
\r
1899 wxTheClipboard->GetData(data);
\r
1900 m_textCtrlAddress->SetValue(data.GetText());
\r
1902 wxTheClipboard->Close();
\r
1906 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1909 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1913 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1915 wxMessageBox(_("Error in amount "), _("Send Coins"));
\r
1918 if (nValue > GetBalance())
\r
1920 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
\r
1923 if (nValue + nTransactionFee > GetBalance())
\r
1925 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
\r
1929 // Parse bitcoin address
\r
1931 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1933 if (fBitcoinAddress)
\r
1935 // Send to bitcoin address
\r
1936 CScript scriptPubKey;
\r
1937 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1939 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
\r
1940 if (strError == "")
\r
1941 wxMessageBox(_("Payment sent "), _("Sending..."));
\r
1942 else if (strError != "ABORTED")
\r
1943 wxMessageBox(strError + " ", _("Sending..."));
\r
1947 // Parse IP address
\r
1948 CAddress addr(strAddress);
\r
1949 if (!addr.IsValid())
\r
1951 wxMessageBox(_("Invalid address "), _("Send Coins"));
\r
1956 wtx.mapValue["to"] = strAddress;
\r
1957 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1958 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1960 // Send to IP address
\r
1961 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1962 if (!pdialog->ShowModal())
\r
1966 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1967 if (!mapAddressBook.count(strAddress))
\r
1968 SetAddressBookName(strAddress, "");
\r
1973 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1984 //////////////////////////////////////////////////////////////////////////////
\r
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
\r
1992 nPrice = nPriceIn;
\r
1994 start = wxDateTime::UNow();
\r
1995 memset(pszStatus, 0, sizeof(pszStatus));
\r
1996 fCanCancel = true;
\r
2000 fWorkDone = false;
\r
2002 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
\r
2005 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
2006 m_textCtrlStatus->SetValue("");
\r
2008 CreateThread(SendingDialogStartTransfer, this);
\r
2011 CSendingDialog::~CSendingDialog()
\r
2013 printf("~CSendingDialog()\n");
\r
2016 void CSendingDialog::Close()
\r
2018 // Last one out turn out the lights.
\r
2019 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
2020 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
2021 // This allows the window to disappear and end modality when cancelled
\r
2022 // without making the user wait for ConnectNode to return. The dialog object
\r
2023 // hangs around in the background until the work thread exits.
\r
2025 EndModal(fSuccess);
\r
2034 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
2036 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
2043 wxCommandEvent cmdevent;
\r
2044 OnButtonCancel(cmdevent);
\r
2048 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
2054 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
2060 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
2063 if (strlen(pszStatus) > 130)
\r
2064 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
2066 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
2067 m_staticTextSending->SetFocus();
\r
2069 m_buttonCancel->Enable(false);
\r
2072 m_buttonOK->Enable(true);
\r
2073 m_buttonOK->SetFocus();
\r
2074 m_buttonCancel->Enable(false);
\r
2076 if (fAbort && fCanCancel && IsShown())
\r
2078 strcpy(pszStatus, _("CANCELLED"));
\r
2079 m_buttonOK->Enable(true);
\r
2080 m_buttonOK->SetFocus();
\r
2081 m_buttonCancel->Enable(false);
\r
2082 m_buttonCancel->SetLabel(_("Cancelled"));
\r
2084 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
\r
2090 // Everything from here on is not in the UI thread and must only communicate
\r
2091 // with the rest of the dialog through variables and calling repaint.
\r
2094 void CSendingDialog::Repaint()
\r
2097 wxPaintEvent event;
\r
2098 GetEventHandler()->AddPendingEvent(event);
\r
2101 bool CSendingDialog::Status()
\r
2108 if (fAbort && fCanCancel)
\r
2110 memset(pszStatus, 0, 10);
\r
2111 strcpy(pszStatus, _("CANCELLED"));
\r
2119 bool CSendingDialog::Status(const string& str)
\r
2124 // This can be read by the UI thread at any time,
\r
2125 // so copy in a way that can be read cleanly at all times.
\r
2126 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
2127 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
2133 bool CSendingDialog::Error(const string& str)
\r
2135 fCanCancel = false;
\r
2137 Status(string(_("Error: ")) + str);
\r
2141 void SendingDialogStartTransfer(void* parg)
\r
2143 ((CSendingDialog*)parg)->StartTransfer();
\r
2146 void CSendingDialog::StartTransfer()
\r
2148 // Make sure we have enough money
\r
2149 if (nPrice + nTransactionFee > GetBalance())
\r
2151 Error(_("Insufficient funds"));
\r
2155 // We may have connected already for product details
\r
2156 if (!Status(_("Connecting...")))
\r
2158 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2161 Error(_("Unable to connect"));
\r
2165 // Send order to seller, with response going to OnReply2 via event handler
\r
2166 if (!Status(_("Requesting public key...")))
\r
2168 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2171 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2173 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2176 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2178 if (!Status(_("Received public key...")))
\r
2181 CScript scriptPubKey;
\r
2188 string strMessage;
\r
2189 vRecv >> strMessage;
\r
2190 Error(_("Transfer was not accepted"));
\r
2191 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2194 vRecv >> scriptPubKey;
\r
2198 //// what do we want to do about this?
\r
2199 Error(_("Invalid response received"));
\r
2203 // Pause to give the user a chance to cancel
\r
2204 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2211 CRITICAL_BLOCK(cs_main)
\r
2214 if (!Status(_("Creating transaction...")))
\r
2216 if (nPrice + nTransactionFee > GetBalance())
\r
2218 Error(_("Insufficient funds"));
\r
2222 int64 nFeeRequired;
\r
2223 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2225 if (nPrice + nFeeRequired > GetBalance())
\r
2226 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
\r
2228 Error(_("Transaction creation failed"));
\r
2232 // Transaction fee
\r
2233 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
\r
2235 Error(_("Transaction aborted"));
\r
2239 // Make sure we're still connected
\r
2240 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2243 Error(_("Lost connection, transaction cancelled"));
\r
2247 // Last chance to cancel
\r
2251 fCanCancel = false;
\r
2254 fCanCancel = true;
\r
2257 fCanCancel = false;
\r
2259 if (!Status(_("Sending payment...")))
\r
2263 if (!CommitTransaction(wtx, key))
\r
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."));
\r
2269 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2270 CWalletTx wtxSend = wtx;
\r
2271 wtxSend.fFromMe = false;
\r
2272 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
\r
2274 Status(_("Waiting for confirmation..."));
\r
2275 MainFrameRepaint();
\r
2279 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2281 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2284 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2292 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
\r
2293 "The transaction is recorded and will credit to the recipient,\n"
\r
2294 "but the comment information will be blank."));
\r
2300 //// what do we want to do about this?
\r
2301 Error(_("Payment was sent, but an invalid response was received"));
\r
2307 Status(_("Payment completed"));
\r
2315 //////////////////////////////////////////////////////////////////////////////
\r
2317 // CAddressBookDialog
\r
2320 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
\r
2322 // Set initially selected page
\r
2323 wxNotebookEvent event;
\r
2324 event.SetSelection(nPageIn);
\r
2325 OnNotebookPageChanged(event);
\r
2326 m_notebook->ChangeSelection(nPageIn);
\r
2328 fDuringSend = fDuringSendIn;
\r
2330 m_buttonCancel->Show(false);
\r
2333 wxIcon iconAddressBook;
\r
2334 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2335 SetIcon(iconAddressBook);
\r
2337 // Init column headers
\r
2338 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
\r
2339 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
\r
2340 m_listCtrlSending->SetFocus();
\r
2341 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
\r
2342 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
\r
2343 m_listCtrlReceiving->SetFocus();
\r
2345 // Fill listctrl with address book data
\r
2346 CRITICAL_BLOCK(cs_mapKeys)
\r
2347 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2349 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
\r
2350 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2352 string strAddress = item.first;
\r
2353 string strName = item.second;
\r
2355 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2356 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
\r
2357 int nIndex = InsertLine(plistCtrl, strName, strAddress);
\r
2358 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
\r
2359 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2364 wxString CAddressBookDialog::GetSelectedAddress()
\r
2366 int nIndex = GetSelection(m_listCtrl);
\r
2369 return GetItemText(m_listCtrl, nIndex, 1);
\r
2372 wxString CAddressBookDialog::GetSelectedSendingAddress()
\r
2374 int nIndex = GetSelection(m_listCtrlSending);
\r
2377 return GetItemText(m_listCtrlSending, nIndex, 1);
\r
2380 wxString CAddressBookDialog::GetSelectedReceivingAddress()
\r
2382 int nIndex = GetSelection(m_listCtrlReceiving);
\r
2385 return GetItemText(m_listCtrlReceiving, nIndex, 1);
\r
2388 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
\r
2391 nPage = event.GetSelection();
\r
2392 if (nPage == SENDING)
\r
2393 m_listCtrl = m_listCtrlSending;
\r
2394 else if (nPage == RECEIVING)
\r
2395 m_listCtrl = m_listCtrlReceiving;
\r
2396 m_buttonDelete->Show(nPage == SENDING);
\r
2397 m_buttonCopy->Show(nPage == RECEIVING);
\r
2399 m_listCtrl->SetFocus();
\r
2402 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2404 // Update address book with edited name
\r
2406 if (event.IsEditCancelled())
\r
2408 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2409 SetAddressBookName(strAddress, string(event.GetText()));
\r
2410 pframeMain->RefreshListCtrl();
\r
2413 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2416 if (nPage == RECEIVING)
\r
2417 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
\r
2420 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2425 // Doubleclick returns selection
\r
2426 EndModal(GetSelectedAddress() != "" ? 2 : 0);
\r
2430 // Doubleclick edits item
\r
2431 wxCommandEvent event2;
\r
2432 OnButtonEdit(event2);
\r
2435 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2437 if (nPage != SENDING)
\r
2439 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2441 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2443 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2444 CWalletDB().EraseName(strAddress);
\r
2445 m_listCtrl->DeleteItem(nIndex);
\r
2448 pframeMain->RefreshListCtrl();
\r
2451 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2453 // Copy address box to clipboard
\r
2454 if (wxTheClipboard->Open())
\r
2456 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
\r
2457 wxTheClipboard->Close();
\r
2461 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2464 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2466 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
\r
2470 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2472 int nIndex = GetSelection(m_listCtrl);
\r
2475 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2476 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2477 string strAddressOrg = strAddress;
\r
2479 if (nPage == SENDING)
\r
2481 // Ask name and address
\r
2484 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
\r
2485 if (!dialog.ShowModal())
\r
2487 strName = dialog.GetValue1();
\r
2488 strAddress = dialog.GetValue2();
\r
2490 while (CheckIfMine(strAddress, _("Edit Address")));
\r
2493 else if (nPage == RECEIVING)
\r
2496 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
\r
2497 if (!dialog.ShowModal())
\r
2499 strName = dialog.GetValue();
\r
2503 if (strAddress != strAddressOrg)
\r
2504 CWalletDB().EraseName(strAddressOrg);
\r
2505 SetAddressBookName(strAddress, strName);
\r
2506 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2507 m_listCtrl->SetItemText(nIndex, strName);
\r
2508 pframeMain->RefreshListCtrl();
\r
2511 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2514 string strAddress;
\r
2516 if (nPage == SENDING)
\r
2518 // Ask name and address
\r
2521 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
\r
2522 if (!dialog.ShowModal())
\r
2524 strName = dialog.GetValue1();
\r
2525 strAddress = dialog.GetValue2();
\r
2527 while (CheckIfMine(strAddress, _("Add Address")));
\r
2529 else if (nPage == RECEIVING)
\r
2532 CGetTextFromUserDialog dialog(this,
\r
2533 _("New Receiving Address"),
\r
2534 _("You should use a new address for each payment you receive.\n\nLabel"),
\r
2536 if (!dialog.ShowModal())
\r
2538 strName = dialog.GetValue();
\r
2540 // Generate new key
\r
2541 strAddress = PubKeyToAddress(GenerateNewKey());
\r
2544 // Add to list and select it
\r
2545 SetAddressBookName(strAddress, strName);
\r
2546 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2547 SetSelection(m_listCtrl, nIndex);
\r
2548 m_listCtrl->SetFocus();
\r
2549 if (nPage == SENDING)
\r
2550 pframeMain->RefreshListCtrl();
\r
2553 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2556 EndModal(GetSelectedAddress() != "" ? 1 : 0);
\r
2559 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2565 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2576 //////////////////////////////////////////////////////////////////////////////
\r
2583 ID_TASKBAR_RESTORE = 10001,
\r
2584 ID_TASKBAR_OPTIONS,
\r
2585 ID_TASKBAR_GENERATE,
\r
2589 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
2590 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
2591 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
2592 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
2593 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
2594 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
2595 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
2598 void CMyTaskBarIcon::Show(bool fShow)
\r
2600 static char pszPrevTip[200];
\r
2603 string strTooltip = _("Bitcoin");
\r
2604 if (fGenerateBitcoins)
\r
2605 strTooltip = _("Bitcoin - Generating");
\r
2606 if (fGenerateBitcoins && vNodes.empty())
\r
2607 strTooltip = _("Bitcoin - (not connected)");
\r
2609 // Optimization, only update when changed, using char array to be reentrant
\r
2610 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
2612 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
2614 // somehow it'll choose the wrong size and scale it down if
\r
2615 // we use the main icon, so we hand it one with only 16x16
\r
2616 SetIcon(wxICON(favicon), strTooltip);
\r
2618 SetIcon(bitcoin80_xpm, strTooltip);
\r
2624 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
2629 void CMyTaskBarIcon::Hide()
\r
2634 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
2639 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
2644 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
2646 // Since it's modal, get the main window to do it
\r
2647 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
\r
2648 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
2651 void CMyTaskBarIcon::Restore()
\r
2653 pframeMain->Show();
\r
2654 wxIconizeEvent event(0, false);
\r
2655 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
2656 pframeMain->Iconize(false);
\r
2657 pframeMain->Raise();
\r
2660 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
2662 GenerateBitcoins(event.IsChecked());
\r
2665 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
2667 event.Check(fGenerateBitcoins);
\r
2670 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
2672 pframeMain->Close(true);
\r
2675 void CMyTaskBarIcon::UpdateTooltip()
\r
2677 if (IsIconInstalled())
\r
2681 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
2683 wxMenu* pmenu = new wxMenu;
\r
2684 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
\r
2685 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
\r
2686 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
\r
2687 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
2688 pmenu->AppendSeparator();
\r
2689 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
\r
2699 //////////////////////////////////////////////////////////////////////////////
\r
2704 void CreateMainWindow()
\r
2706 pframeMain = new CMainFrame(NULL);
\r
2707 if (mapArgs.count("-min"))
\r
2708 pframeMain->Iconize(true);
\r
2709 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
\r
2710 if (!mapArgs.count("-minimizetotray"))
\r
2711 fMinimizeToTray = false;
\r
2713 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
2714 if (fMinimizeToTray && pframeMain->IsIconized())
\r
2715 fClosedToTray = true;
\r
2716 pframeMain->Show(!fClosedToTray);
\r
2717 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
2718 CreateThread(ThreadDelayedRepaint, NULL);
\r
2722 // Define a new application
\r
2723 class CMyApp : public wxApp
\r
2732 // Hook Initialize so we can start without GUI
\r
2733 virtual bool Initialize(int& argc, wxChar** argv);
\r
2735 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
2736 // event handler here
\r
2737 virtual bool OnExceptionInMainLoop();
\r
2739 // 3rd, and final, level exception handling: whenever an unhandled
\r
2740 // exception is caught, this function is called
\r
2741 virtual void OnUnhandledException();
\r
2743 // and now for something different: this function is called in case of a
\r
2744 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
2745 virtual void OnFatalException();
\r
2748 IMPLEMENT_APP(CMyApp)
\r
2750 bool CMyApp::Initialize(int& argc, wxChar** argv)
\r
2752 for (int i = 1; i < argc; i++)
\r
2753 if (!IsSwitchChar(argv[i][0]))
\r
2754 fCommandLine = true;
\r
2756 if (!fCommandLine)
\r
2758 // wxApp::Initialize will remove environment-specific parameters,
\r
2759 // so it's too early to call ParseParameters yet
\r
2760 for (int i = 1; i < argc; i++)
\r
2762 wxString str = argv[i];
\r
2764 if (str.size() >= 1 && str[0] == '/')
\r
2766 char pszLower[MAX_PATH];
\r
2767 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
\r
2771 if (str == "-daemon")
\r
2777 if (fDaemon || fCommandLine)
\r
2779 // Call the original Initialize while suppressing error messages
\r
2780 // and ignoring failure. If unable to initialize GTK, it fails
\r
2781 // near the end so hopefully the last few things don't matter.
\r
2784 wxApp::Initialize(argc, argv);
\r
2790 pid_t pid = fork();
\r
2793 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
\r
2797 pthread_exit((void*)0);
\r
2804 return wxApp::Initialize(argc, argv);
\r
2807 bool CMyApp::OnInit()
\r
2809 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
\r
2810 // Disable malfunctioning wxWidgets debug assertion
\r
2811 extern int g_isPainting;
\r
2812 g_isPainting = 10000;
\r
2815 wxImage::AddHandler(new wxPNGHandler);
\r
2817 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
\r
2818 SetAppName("Bitcoin");
\r
2820 SetAppName("bitcoin");
\r
2824 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
\r
2825 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
\r
2826 class wxMBConv_win32 : public wxMBConv
\r
2830 size_t m_minMBCharWidth;
\r
2832 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
\r
2833 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
\r
2837 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
\r
2838 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
\r
2839 g_locale.AddCatalogLookupPathPrefix("locale");
\r
2841 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
\r
2842 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
\r
2844 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
\r
2845 g_locale.AddCatalog("bitcoin");
\r
2847 return AppInit(argc, argv);
\r
2850 int CMyApp::OnExit()
\r
2853 return wxApp::OnExit();
\r
2856 bool CMyApp::OnExceptionInMainLoop()
\r
2862 catch (std::exception& e)
\r
2864 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
2865 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2871 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
2872 wxLogWarning("Unknown exception");
\r
2879 void CMyApp::OnUnhandledException()
\r
2881 // this shows how we may let some exception propagate uncaught
\r
2886 catch (std::exception& e)
\r
2888 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
2889 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2895 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
2896 wxLogWarning("Unknown exception");
\r
2902 void CMyApp::OnFatalException()
\r
2904 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);
\r