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 (pframeMain && pframeMain->m_statusBar)
\r
200 pframeMain->m_statusBar->SetStatusText(strText, nField);
\r
203 void SetDefaultReceivingAddress(const string& strAddress)
\r
205 // Update main window address and database
\r
206 if (pframeMain == NULL)
\r
208 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
\r
211 if (!AddressToHash160(strAddress, hash160))
\r
213 if (!mapPubKeys.count(hash160))
\r
215 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
216 pframeMain->m_textCtrlAddress->SetValue(strAddress);
\r
229 //////////////////////////////////////////////////////////////////////////////
\r
234 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
236 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
238 // Set initially selected page
\r
239 wxNotebookEvent event;
\r
240 event.SetSelection(0);
\r
241 OnNotebookPageChanged(event);
\r
242 m_notebook->ChangeSelection(0);
\r
245 fRefreshListCtrl = false;
\r
246 fRefreshListCtrlRunning = false;
\r
247 fOnSetFocusAddress = false;
\r
249 m_choiceFilter->SetSelection(0);
\r
250 double dResize = 1.0;
\r
252 SetIcon(wxICON(bitcoin));
\r
254 SetIcon(bitcoin80_xpm);
\r
255 SetBackgroundColour(m_toolBar->GetBackgroundColour());
\r
256 wxFont fontTmp = m_staticText41->GetFont();
\r
257 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
258 m_staticTextBalance->SetFont(fontTmp);
\r
259 m_staticTextBalance->SetSize(140, 17);
\r
260 // resize to fit ubuntu's huge default font
\r
262 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
\r
264 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
265 m_listCtrl->SetFocus();
\r
266 ptaskbaricon = new CMyTaskBarIcon();
\r
267 #ifdef __WXMAC_OSX__
\r
268 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
\r
269 // to their standard places, leaving these menus empty.
\r
270 GetMenuBar()->Remove(2); // remove Help menu
\r
271 GetMenuBar()->Remove(0); // remove File menu
\r
274 // Init column headers
\r
275 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
276 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
278 #ifdef __WXMAC_OSX__
\r
282 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
\r
283 foreach(wxListCtrl* p, pplistCtrl)
\r
285 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
286 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
287 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
\r
288 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
289 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
290 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
291 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
295 int pnWidths[3] = { -100, 88, 300 };
\r
297 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
298 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
300 m_statusBar->SetFieldsCount(3, pnWidths);
\r
302 // Fill your address text box
\r
303 vector<unsigned char> vchPubKey;
\r
304 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
305 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
307 // Fill listctrl with wallet transactions
\r
311 CMainFrame::~CMainFrame()
\r
314 delete ptaskbaricon;
\r
315 ptaskbaricon = NULL;
\r
318 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
\r
321 nPage = event.GetSelection();
\r
324 m_listCtrl = m_listCtrlAll;
\r
325 fShowGenerated = true;
\r
327 fShowReceived = true;
\r
329 else if (nPage == SENTRECEIVED)
\r
331 m_listCtrl = m_listCtrlSentReceived;
\r
332 fShowGenerated = false;
\r
334 fShowReceived = true;
\r
336 else if (nPage == SENT)
\r
338 m_listCtrl = m_listCtrlSent;
\r
339 fShowGenerated = false;
\r
341 fShowReceived = false;
\r
343 else if (nPage == RECEIVED)
\r
345 m_listCtrl = m_listCtrlReceived;
\r
346 fShowGenerated = false;
\r
348 fShowReceived = true;
\r
351 m_listCtrl->SetFocus();
\r
354 void CMainFrame::OnClose(wxCloseEvent& event)
\r
356 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
358 // Divert close to minimize
\r
360 fClosedToTray = true;
\r
366 CreateThread(Shutdown, NULL);
\r
370 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
373 // Hide the task bar button when minimized.
\r
374 // Event is sent when the frame is minimized or restored.
\r
375 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
376 // to get rid of the deprecated warning. Just ignore it.
\r
377 if (!event.Iconized())
\r
378 fClosedToTray = false;
\r
380 if (mapArgs.count("-minimizetotray")) {
\r
382 // The tray icon sometimes disappears on ubuntu karmic
\r
383 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
\r
384 // Reports of CPU peg on 64-bit linux
\r
385 if (fMinimizeToTray && event.Iconized())
\r
386 fClosedToTray = true;
\r
387 Show(!fClosedToTray);
\r
388 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
394 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
398 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
399 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
402 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
404 // Hidden columns not resizeable
\r
405 if (event.GetColumn() <= 1 && !fDebug)
\r
411 int CMainFrame::GetSortIndex(const string& strSort)
\r
416 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
417 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
418 // In the wx generic implementation, they store the list of items
\r
419 // in a vector, so indexed lookups are fast, but inserts are slower
\r
420 // the closer they are to the top.
\r
422 int high = m_listCtrl->GetItemCount();
\r
425 int mid = low + ((high - low) / 2);
\r
426 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
435 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
437 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
\r
438 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
\r
441 if (!fNew && nIndex == -1)
\r
443 string strHash = " " + hashKey.ToString();
\r
444 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
445 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
449 // fNew is for blind insert, only use if you're sure it's new
\r
450 if (fNew || nIndex == -1)
\r
452 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
456 // If sort key changed, must delete and reinsert to make it relocate
\r
457 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
\r
459 m_listCtrl->DeleteItem(nIndex);
\r
460 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
464 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
\r
465 m_listCtrl->SetItem(nIndex, 2, str2);
\r
466 m_listCtrl->SetItem(nIndex, 3, str3);
\r
467 m_listCtrl->SetItem(nIndex, 4, str4);
\r
468 m_listCtrl->SetItem(nIndex, 5, str5);
\r
469 m_listCtrl->SetItem(nIndex, 6, str6);
\r
470 m_listCtrl->SetItemData(nIndex, nData);
\r
473 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
475 long nData = *(long*)&hashKey;
\r
479 string strHash = " " + hashKey.ToString();
\r
480 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
481 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
485 m_listCtrl->DeleteItem(nIndex);
\r
487 return nIndex != -1;
\r
490 string FormatTxStatus(const CWalletTx& wtx)
\r
493 if (!wtx.IsFinal())
\r
495 if (wtx.nLockTime < 500000000)
\r
496 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
\r
498 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
\r
502 int nDepth = wtx.GetDepthInMainChain();
\r
503 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
504 return strprintf(_("%d/offline?"), nDepth);
\r
505 else if (nDepth < 6)
\r
506 return strprintf(_("%d/unconfirmed"), nDepth);
\r
508 return strprintf(_("%d confirmations"), nDepth);
\r
512 string SingleLine(const string& strIn)
\r
515 bool fOneSpace = false;
\r
516 foreach(int c, strIn)
\r
524 if (fOneSpace && !strOut.empty())
\r
533 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
535 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
536 int64 nCredit = wtx.GetCredit(true);
\r
537 int64 nDebit = wtx.GetDebit();
\r
538 int64 nNet = nCredit - nDebit;
\r
539 uint256 hash = wtx.GetHash();
\r
540 string strStatus = FormatTxStatus(wtx);
\r
541 map<string, string> mapValue = wtx.mapValue;
\r
542 wtx.nLinesDisplayed = 1;
\r
543 nListViewUpdated++;
\r
546 if (wtx.IsCoinBase())
\r
548 // Don't show generated coin until confirmed by at least one block after it
\r
549 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
551 // It is not an error when generated blocks are not accepted. By design,
\r
552 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
553 // This is the normal mechanism by which the network copes with latency.
\r
555 // We display regular transactions right away before any confirmation
\r
556 // because they can always get into some block eventually. Generated coins
\r
557 // are special because if their block is not accepted, they are not valid.
\r
559 if (wtx.GetDepthInMainChain() < 2)
\r
561 wtx.nLinesDisplayed = 0;
\r
565 if (!fShowGenerated)
\r
569 // Find the block the tx is in
\r
570 CBlockIndex* pindex = NULL;
\r
571 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
572 if (mi != mapBlockIndex.end())
\r
573 pindex = (*mi).second;
\r
575 // Sort order, unrecorded transactions sort to the top
\r
576 string strSort = strprintf("%010d-%01d-%010u",
\r
577 (pindex ? pindex->nHeight : INT_MAX),
\r
578 (wtx.IsCoinBase() ? 1 : 0),
\r
579 wtx.nTimeReceived);
\r
582 if (nNet > 0 || wtx.IsCoinBase())
\r
587 string strDescription;
\r
588 if (wtx.IsCoinBase())
\r
591 strDescription = _("Generated");
\r
594 int64 nUnmatured = 0;
\r
595 foreach(const CTxOut& txout, wtx.vout)
\r
596 nUnmatured += txout.GetCredit();
\r
597 if (wtx.IsInMainChain())
\r
599 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
601 // Check if the block was requested by anyone
\r
602 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
603 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
\r
607 strDescription = _("Generated (not accepted)");
\r
611 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
613 // Received by IP connection
\r
614 if (!fShowReceived)
\r
616 if (!mapValue["from"].empty())
\r
617 strDescription += _("From: ") + mapValue["from"];
\r
618 if (!mapValue["message"].empty())
\r
620 if (!strDescription.empty())
\r
621 strDescription += " - ";
\r
622 strDescription += mapValue["message"];
\r
627 // Received by Bitcoin Address
\r
628 if (!fShowReceived)
\r
630 foreach(const CTxOut& txout, wtx.vout)
\r
632 if (txout.IsMine())
\r
634 vector<unsigned char> vchPubKey;
\r
635 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
637 CRITICAL_BLOCK(cs_mapAddressBook)
\r
639 //strDescription += _("Received payment to ");
\r
640 //strDescription += _("Received with address ");
\r
641 strDescription += _("From: unknown, Received with: ");
\r
642 string strAddress = PubKeyToAddress(vchPubKey);
\r
643 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
\r
644 if (mi != mapAddressBook.end() && !(*mi).second.empty())
\r
646 string strLabel = (*mi).second;
\r
647 strDescription += strAddress.substr(0,12) + "... ";
\r
648 strDescription += "(" + strLabel + ")";
\r
651 strDescription += strAddress;
\r
659 InsertLine(fNew, nIndex, hash, strSort,
\r
661 nTime ? DateTimeStr(nTime) : "",
\r
662 SingleLine(strDescription),
\r
664 FormatMoney(nNet, true));
\r
668 bool fAllFromMe = true;
\r
669 foreach(const CTxIn& txin, wtx.vin)
\r
670 fAllFromMe = fAllFromMe && txin.IsMine();
\r
672 bool fAllToMe = true;
\r
673 foreach(const CTxOut& txout, wtx.vout)
\r
674 fAllToMe = fAllToMe && txout.IsMine();
\r
676 if (fAllFromMe && fAllToMe)
\r
679 int64 nValue = wtx.vout[0].nValue;
\r
680 InsertLine(fNew, nIndex, hash, strSort,
\r
682 nTime ? DateTimeStr(nTime) : "",
\r
683 _("Payment to yourself"),
\r
686 /// issue: can't tell which is the payment and which is the change anymore
\r
687 // FormatMoney(nNet - nValue, true),
\r
688 // FormatMoney(nValue, true));
\r
690 else if (fAllFromMe)
\r
698 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
699 wtx.nLinesDisplayed = 0;
\r
700 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
702 const CTxOut& txout = wtx.vout[nOut];
\r
703 if (txout.IsMine())
\r
707 if (!mapValue["to"].empty())
\r
710 strAddress = mapValue["to"];
\r
714 // Sent to Bitcoin Address
\r
716 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
717 strAddress = Hash160ToAddress(hash160);
\r
720 string strDescription = _("To: ");
\r
721 CRITICAL_BLOCK(cs_mapAddressBook)
\r
722 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
723 strDescription += mapAddressBook[strAddress] + " ";
\r
724 strDescription += strAddress;
\r
725 if (!mapValue["message"].empty())
\r
727 if (!strDescription.empty())
\r
728 strDescription += " - ";
\r
729 strDescription += mapValue["message"];
\r
732 int64 nValue = txout.nValue;
\r
739 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
741 nTime ? DateTimeStr(nTime) : "",
\r
742 SingleLine(strDescription),
\r
743 FormatMoney(-nValue, true),
\r
745 wtx.nLinesDisplayed++;
\r
751 // Mixed debit transaction, can't break down payees
\r
753 bool fAllMine = true;
\r
754 foreach(const CTxOut& txout, wtx.vout)
\r
755 fAllMine = fAllMine && txout.IsMine();
\r
756 foreach(const CTxIn& txin, wtx.vin)
\r
757 fAllMine = fAllMine && txin.IsMine();
\r
759 InsertLine(fNew, nIndex, hash, strSort,
\r
761 nTime ? DateTimeStr(nTime) : "",
\r
763 FormatMoney(nNet, true),
\r
771 void CMainFrame::RefreshListCtrl()
\r
773 fRefreshListCtrl = true;
\r
777 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
779 if (fRefreshListCtrl)
\r
781 // Collect list of wallet transactions and sort newest first
\r
782 bool fEntered = false;
\r
783 vector<pair<unsigned int, uint256> > vSorted;
\r
784 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
786 printf("RefreshListCtrl starting\n");
\r
788 fRefreshListCtrl = false;
\r
789 vWalletUpdated.clear();
\r
791 // Do the newest transactions first
\r
792 vSorted.reserve(mapWallet.size());
\r
793 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
795 const CWalletTx& wtx = (*it).second;
\r
796 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
797 vSorted.push_back(make_pair(nTime, (*it).first));
\r
799 m_listCtrl->DeleteAllItems();
\r
804 sort(vSorted.begin(), vSorted.end());
\r
806 // Fill list control
\r
807 for (int i = 0; i < vSorted.size();)
\r
811 bool fEntered = false;
\r
812 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
815 uint256& hash = vSorted[i++].second;
\r
816 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
817 if (mi != mapWallet.end())
\r
818 InsertTransaction((*mi).second, true);
\r
820 if (!fEntered || i == 100 || i % 500 == 0)
\r
824 printf("RefreshListCtrl done\n");
\r
826 // Update transaction total display
\r
827 MainFrameRepaint();
\r
831 // Check for time updates
\r
832 static int64 nLastTime;
\r
833 if (GetTime() > nLastTime + 30)
\r
835 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
837 nLastTime = GetTime();
\r
838 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
840 CWalletTx& wtx = (*it).second;
\r
841 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
842 InsertTransaction(wtx, false);
\r
849 void CMainFrame::RefreshStatusColumn()
\r
851 static int nLastTop;
\r
852 static CBlockIndex* pindexLastBest;
\r
853 static unsigned int nLastRefreshed;
\r
855 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
856 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
859 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
862 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
864 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
866 // If no updates, only need to do the part that moved onto the screen
\r
867 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
868 nStart = nLastTop + 100;
\r
869 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
873 pindexLastBest = pindexBest;
\r
874 nLastRefreshed = nListViewUpdated;
\r
876 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
878 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
879 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
880 if (mi == mapWallet.end())
\r
882 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
885 CWalletTx& wtx = (*mi).second;
\r
886 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
888 if (!InsertTransaction(wtx, false, nIndex))
\r
889 m_listCtrl->DeleteItem(nIndex--);
\r
892 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
897 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
908 unsigned int nNeedRepaint = 0;
\r
909 unsigned int nLastRepaint = 0;
\r
910 int64 nLastRepaintTime = 0;
\r
911 int64 nRepaintInterval = 500;
\r
913 void ThreadDelayedRepaint(void* parg)
\r
917 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
919 nLastRepaint = nNeedRepaint;
\r
922 printf("DelayedRepaint\n");
\r
923 wxPaintEvent event;
\r
924 pframeMain->fRefresh = true;
\r
925 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
928 Sleep(nRepaintInterval);
\r
932 void MainFrameRepaint()
\r
934 // This is called by network code that shouldn't access pframeMain
\r
935 // directly because it could still be running after the UI is closed.
\r
938 // Don't repaint too often
\r
939 static int64 nLastRepaintRequest;
\r
940 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
945 nLastRepaintRequest = GetTimeMillis();
\r
947 printf("MainFrameRepaint\n");
\r
948 wxPaintEvent event;
\r
949 pframeMain->fRefresh = true;
\r
950 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
954 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
956 // Skip lets the listctrl do the paint, we're just hooking the message
\r
960 ptaskbaricon->UpdateTooltip();
\r
965 static int nTransactionCount;
\r
966 bool fPaintedBalance = false;
\r
967 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
969 nLastRepaint = nNeedRepaint;
\r
970 nLastRepaintTime = GetTimeMillis();
\r
972 // Update listctrl contents
\r
973 if (!vWalletUpdated.empty())
\r
975 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
978 if (m_listCtrl->GetItemCount())
\r
979 strTop = (string)m_listCtrl->GetItemText(0);
\r
980 foreach(uint256 hash, vWalletUpdated)
\r
982 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
983 if (mi != mapWallet.end())
\r
984 InsertTransaction((*mi).second, false);
\r
986 vWalletUpdated.clear();
\r
987 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
988 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
993 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
995 fPaintedBalance = true;
\r
996 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
998 // Count hidden and multi-line transactions
\r
999 nTransactionCount = 0;
\r
1000 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
1002 CWalletTx& wtx = (*it).second;
\r
1003 nTransactionCount += wtx.nLinesDisplayed;
\r
1007 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1010 // Update status column of visible items only
\r
1011 RefreshStatusColumn();
\r
1013 // Update status bar
\r
1014 string strGen = "";
\r
1015 if (fGenerateBitcoins)
\r
1016 strGen = _(" Generating");
\r
1017 if (fGenerateBitcoins && vNodes.empty())
\r
1018 strGen = _("(not connected)");
\r
1019 m_statusBar->SetStatusText(strGen, 1);
\r
1021 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1022 m_statusBar->SetStatusText(strStatus, 2);
\r
1024 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1025 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1027 // Update receiving address
\r
1028 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
\r
1029 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
\r
1030 m_textCtrlAddress->SetValue(strDefaultAddress);
\r
1034 void UIThreadCall(boost::function0<void> fn)
\r
1036 // Call this with a function object created with bind.
\r
1037 // bind needs all parameters to match the function's expected types
\r
1038 // and all default parameters specified. Some examples:
\r
1039 // UIThreadCall(bind(wxBell));
\r
1040 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1041 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1044 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1045 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1046 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1050 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1052 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1057 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1063 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1065 // Options->Generate Coins
\r
1066 GenerateBitcoins(event.IsChecked());
\r
1069 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1071 event.Check(fGenerateBitcoins);
\r
1074 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1076 // Options->Your Receiving Addresses
\r
1077 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
\r
1078 if (!dialog.ShowModal())
\r
1082 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1084 // Options->Options
\r
1085 COptionsDialog dialog(this);
\r
1086 dialog.ShowModal();
\r
1089 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1092 CAboutDialog dialog(this);
\r
1093 dialog.ShowModal();
\r
1096 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1099 CSendDialog dialog(this);
\r
1100 dialog.ShowModal();
\r
1103 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1105 // Toolbar: Address Book
\r
1106 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
\r
1107 if (dialogAddr.ShowModal() == 2)
\r
1110 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
\r
1111 dialogSend.ShowModal();
\r
1115 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1117 // Automatically select-all when entering window
\r
1119 m_textCtrlAddress->SetSelection(-1, -1);
\r
1120 fOnSetFocusAddress = true;
\r
1123 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1126 if (fOnSetFocusAddress)
\r
1127 m_textCtrlAddress->SetSelection(-1, -1);
\r
1128 fOnSetFocusAddress = false;
\r
1131 void CMainFrame::OnButtonNew(wxCommandEvent& event)
\r
1134 CGetTextFromUserDialog dialog(this,
\r
1135 _("New Receiving Address"),
\r
1136 _("You should use a new address for each payment you receive.\n\nLabel"),
\r
1138 if (!dialog.ShowModal())
\r
1140 string strName = dialog.GetValue();
\r
1142 // Generate new key
\r
1143 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
1146 SetAddressBookName(strAddress, strName);
\r
1147 SetDefaultReceivingAddress(strAddress);
\r
1150 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1152 // Copy address box to clipboard
\r
1153 if (wxTheClipboard->Open())
\r
1155 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1156 wxTheClipboard->Close();
\r
1160 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1162 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1164 CRITICAL_BLOCK(cs_mapWallet)
\r
1166 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1167 if (mi == mapWallet.end())
\r
1169 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1172 wtx = (*mi).second;
\r
1174 CTxDetailsDialog dialog(this, wtx);
\r
1175 dialog.ShowModal();
\r
1176 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1177 //pdialog->Show();
\r
1185 //////////////////////////////////////////////////////////////////////////////
\r
1187 // CTxDetailsDialog
\r
1190 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1192 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1195 strHTML.reserve(4000);
\r
1196 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1198 int64 nTime = wtx.GetTxTime();
\r
1199 int64 nCredit = wtx.GetCredit();
\r
1200 int64 nDebit = wtx.GetDebit();
\r
1201 int64 nNet = nCredit - nDebit;
\r
1205 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
\r
1206 int nRequests = wtx.GetRequestCount();
\r
1207 if (nRequests != -1)
\r
1209 if (nRequests == 0)
\r
1210 strHTML += _(", has not been successfully broadcast yet");
\r
1211 else if (nRequests == 1)
\r
1212 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
\r
1214 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
\r
1216 strHTML += "<br>";
\r
1218 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1224 if (wtx.IsCoinBase())
\r
1226 strHTML += _("<b>Source:</b> Generated<br>");
\r
1228 else if (!wtx.mapValue["from"].empty())
\r
1230 // Online transaction
\r
1231 if (!wtx.mapValue["from"].empty())
\r
1232 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1236 // Offline transaction
\r
1240 foreach(const CTxOut& txout, wtx.vout)
\r
1242 if (txout.IsMine())
\r
1244 vector<unsigned char> vchPubKey;
\r
1245 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1247 string strAddress = PubKeyToAddress(vchPubKey);
\r
1248 if (mapAddressBook.count(strAddress))
\r
1250 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
\r
1251 strHTML += _("<b>To:</b> ");
\r
1252 strHTML += HtmlEscape(strAddress);
\r
1253 if (!mapAddressBook[strAddress].empty())
\r
1254 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
\r
1256 strHTML += _(" (yours)");
\r
1257 strHTML += "<br>";
\r
1270 string strAddress;
\r
1271 if (!wtx.mapValue["to"].empty())
\r
1273 // Online transaction
\r
1274 strAddress = wtx.mapValue["to"];
\r
1275 strHTML += _("<b>To:</b> ");
\r
1276 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1277 strHTML += mapAddressBook[strAddress] + " ";
\r
1278 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1285 if (wtx.IsCoinBase() && nCredit == 0)
\r
1290 int64 nUnmatured = 0;
\r
1291 foreach(const CTxOut& txout, wtx.vout)
\r
1292 nUnmatured += txout.GetCredit();
\r
1293 strHTML += _("<b>Credit:</b> ");
\r
1294 if (wtx.IsInMainChain())
\r
1295 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1297 strHTML += _("(not accepted)");
\r
1298 strHTML += "<br>";
\r
1300 else if (nNet > 0)
\r
1305 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
\r
1309 bool fAllFromMe = true;
\r
1310 foreach(const CTxIn& txin, wtx.vin)
\r
1311 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1313 bool fAllToMe = true;
\r
1314 foreach(const CTxOut& txout, wtx.vout)
\r
1315 fAllToMe = fAllToMe && txout.IsMine();
\r
1322 foreach(const CTxOut& txout, wtx.vout)
\r
1324 if (txout.IsMine())
\r
1327 if (wtx.mapValue["to"].empty())
\r
1329 // Offline transaction
\r
1331 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1333 string strAddress = Hash160ToAddress(hash160);
\r
1334 strHTML += _("<b>To:</b> ");
\r
1335 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1336 strHTML += mapAddressBook[strAddress] + " ";
\r
1337 strHTML += strAddress;
\r
1338 strHTML += "<br>";
\r
1342 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
\r
1347 // Payment to self
\r
1348 /// issue: can't tell which is the payment and which is the change anymore
\r
1349 //int64 nValue = wtx.vout[0].nValue;
\r
1350 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
\r
1351 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
\r
1354 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1356 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
\r
1361 // Mixed debit transaction
\r
1363 foreach(const CTxIn& txin, wtx.vin)
\r
1364 if (txin.IsMine())
\r
1365 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1366 foreach(const CTxOut& txout, wtx.vout)
\r
1367 if (txout.IsMine())
\r
1368 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
\r
1372 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
\r
1378 if (!wtx.mapValue["message"].empty())
\r
1379 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1381 if (wtx.IsCoinBase())
\r
1382 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
1390 strHTML += "<hr><br>debug print<br><br>";
\r
1391 foreach(const CTxIn& txin, wtx.vin)
\r
1392 if (txin.IsMine())
\r
1393 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1394 foreach(const CTxOut& txout, wtx.vout)
\r
1395 if (txout.IsMine())
\r
1396 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1398 strHTML += "<b>Inputs:</b><br>";
\r
1399 CRITICAL_BLOCK(cs_mapWallet)
\r
1401 foreach(const CTxIn& txin, wtx.vin)
\r
1403 COutPoint prevout = txin.prevout;
\r
1404 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1405 if (mi != mapWallet.end())
\r
1407 const CWalletTx& prev = (*mi).second;
\r
1408 if (prevout.n < prev.vout.size())
\r
1410 strHTML += HtmlEscape(prev.ToString(), true);
\r
1411 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1412 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1418 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1419 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1424 strHTML += "</font></html>";
\r
1425 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1426 m_htmlWin->SetPage(strHTML);
\r
1427 m_buttonOK->SetFocus();
\r
1431 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1442 //////////////////////////////////////////////////////////////////////////////
\r
1448 string StartupShortcutPath()
\r
1450 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
1453 bool GetStartOnSystemStartup()
\r
1455 return filesystem::exists(StartupShortcutPath().c_str());
\r
1458 void SetStartOnSystemStartup(bool fAutoStart)
\r
1460 // If the shortcut exists already, remove it for updating
\r
1461 remove(StartupShortcutPath().c_str());
\r
1465 CoInitialize(NULL);
\r
1467 // Get a pointer to the IShellLink interface.
\r
1468 IShellLink* psl = NULL;
\r
1469 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
1470 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
1471 reinterpret_cast<void**>(&psl));
\r
1473 if (SUCCEEDED(hres))
\r
1475 // Get the current executable path
\r
1476 TCHAR pszExePath[MAX_PATH];
\r
1477 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
1479 // Set the path to the shortcut target
\r
1480 psl->SetPath(pszExePath);
\r
1481 PathRemoveFileSpec(pszExePath);
\r
1482 psl->SetWorkingDirectory(pszExePath);
\r
1483 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
1485 // Query IShellLink for the IPersistFile interface for
\r
1486 // saving the shortcut in persistent storage.
\r
1487 IPersistFile* ppf = NULL;
\r
1488 hres = psl->QueryInterface(IID_IPersistFile,
\r
1489 reinterpret_cast<void**>(&ppf));
\r
1490 if (SUCCEEDED(hres))
\r
1492 WCHAR pwsz[MAX_PATH];
\r
1493 // Ensure that the string is ANSI.
\r
1494 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
1495 // Save the link by calling IPersistFile::Save.
\r
1496 hres = ppf->Save(pwsz, TRUE);
\r
1505 #elif defined(__WXGTK__)
\r
1507 // Follow the Desktop Application Autostart Spec:
\r
1508 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
\r
1510 boost::filesystem::path GetAutostartDir()
\r
1512 namespace fs = boost::filesystem;
\r
1514 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
\r
1515 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
\r
1516 char* pszHome = getenv("HOME");
\r
1517 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
\r
1518 return fs::path();
\r
1521 boost::filesystem::path GetAutostartFilePath()
\r
1523 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
\r
1526 bool GetStartOnSystemStartup()
\r
1528 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
\r
1529 if (!optionFile.good())
\r
1531 // Scan through file for "Hidden=true":
\r
1533 while (!optionFile.eof())
\r
1535 getline(optionFile, line);
\r
1536 if (line.find("Hidden") != string::npos &&
\r
1537 line.find("true") != string::npos)
\r
1540 optionFile.close();
\r
1545 void SetStartOnSystemStartup(bool fAutoStart)
\r
1549 unlink(GetAutostartFilePath().native_file_string().c_str());
\r
1553 boost::filesystem::create_directories(GetAutostartDir());
\r
1555 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
\r
1556 if (!optionFile.good())
\r
1558 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
\r
1561 // Write a bitcoin.desktop file to the autostart directory:
\r
1562 char pszExePath[MAX_PATH+1];
\r
1563 memset(pszExePath, 0, sizeof(pszExePath));
\r
1564 readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1);
\r
1565 optionFile << "[Desktop Entry]\n";
\r
1566 optionFile << "Type=Application\n";
\r
1567 optionFile << "Name=Bitcoin\n";
\r
1568 optionFile << "Exec=" << pszExePath << "\n";
\r
1569 optionFile << "Terminal=false\n";
\r
1570 optionFile << "Hidden=false\n";
\r
1571 optionFile.close();
\r
1576 // TODO: OSX startup stuff; see:
\r
1577 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
\r
1579 bool GetStartOnSystemStartup() { return false; }
\r
1580 void SetStartOnSystemStartup(bool fAutoStart) { }
\r
1589 //////////////////////////////////////////////////////////////////////////////
\r
1594 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1596 // Set up list box of page choices
\r
1597 m_listBox->Append(_("Main"));
\r
1598 //m_listBox->Append(_("Test 2"));
\r
1599 m_listBox->SetSelection(0);
\r
1602 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
\r
1603 if (!mapArgs.count("-minimizetotray"))
\r
1605 // Minimize to tray is just too buggy on Linux
\r
1606 fMinimizeToTray = false;
\r
1607 m_checkBoxMinimizeToTray->SetValue(false);
\r
1608 m_checkBoxMinimizeToTray->Enable(false);
\r
1609 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
\r
1612 #ifdef __WXMAC_OSX__
\r
1613 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1617 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1618 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1619 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1620 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1621 int nProcessors = wxThread::GetCPUCount();
\r
1622 if (nProcessors < 1)
\r
1623 nProcessors = 999;
\r
1624 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1625 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1626 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1627 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1628 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1629 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1630 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1631 m_staticTextProxyIP->Enable(fUseProxy);
\r
1632 m_staticTextProxyPort->Enable(fUseProxy);
\r
1633 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1634 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1636 m_buttonOK->SetFocus();
\r
1639 void COptionsDialog::SelectPage(int nPage)
\r
1641 m_panelMain->Show(nPage == 0);
\r
1642 m_panelTest2->Show(nPage == 1);
\r
1644 m_scrolledWindow->Layout();
\r
1645 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1648 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1650 SelectPage(event.GetSelection());
\r
1653 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1656 int64 nTmp = nTransactionFee;
\r
1657 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1658 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1661 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1663 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1666 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1668 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1669 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1670 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1671 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1674 CAddress COptionsDialog::GetProxyAddr()
\r
1676 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1677 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1678 if (addr.ip == INADDR_NONE)
\r
1679 addr.ip = addrProxy.ip;
\r
1680 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1681 addr.port = htons(nPort);
\r
1682 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1683 addr.port = addrProxy.port;
\r
1687 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1690 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1691 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1695 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1697 OnButtonApply(event);
\r
1701 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1706 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1708 CWalletDB walletdb;
\r
1710 int64 nPrevTransactionFee = nTransactionFee;
\r
1711 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1712 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1714 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1715 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1717 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1718 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1720 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1722 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1723 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1725 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1726 GenerateBitcoins(fGenerateBitcoins);
\r
1728 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1730 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1731 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1734 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1736 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1737 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1738 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1741 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1743 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1744 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1747 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1748 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1750 addrProxy = GetProxyAddr();
\r
1751 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1759 //////////////////////////////////////////////////////////////////////////////
\r
1764 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1766 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d beta"), VERSION/10000, (VERSION/100)%100, VERSION%100));
\r
1767 //m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d%s beta"), VERSION/10000, (VERSION/100)%100, VERSION%100, pszSubVer));
\r
1769 // Change (c) into UTF-8 or ANSI copyright symbol
\r
1770 wxString str = m_staticTextMain->GetLabel();
\r
1772 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
\r
1774 str.Replace("(c)", "\xA9");
\r
1776 m_staticTextMain->SetLabel(str);
\r
1778 // Resize on Linux to make the window fit the text.
\r
1779 // The text was wrapped manually rather than using the Wrap setting because
\r
1780 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1781 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1782 if (fontTmp.GetPointSize() > 8);
\r
1783 fontTmp.SetPointSize(8);
\r
1784 m_staticTextMain->SetFont(fontTmp);
\r
1785 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
\r
1789 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1799 //////////////////////////////////////////////////////////////////////////////
\r
1804 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1807 m_textCtrlAddress->SetValue(strAddress);
\r
1808 m_choiceTransferType->SetSelection(0);
\r
1809 m_bitmapCheckMark->Show(false);
\r
1810 fEnabledPrev = true;
\r
1811 m_textCtrlAddress->SetFocus();
\r
1812 //// todo: should add a display of your balance for convenience
\r
1814 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1815 if (fontTmp.GetPointSize() > 9);
\r
1816 fontTmp.SetPointSize(9);
\r
1817 m_staticTextInstructions->SetFont(fontTmp);
\r
1818 SetSize(725, 380);
\r
1823 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1824 SetIcon(iconSend);
\r
1826 wxCommandEvent event;
\r
1827 OnTextAddress(event);
\r
1829 // Fixup the tab order
\r
1830 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1831 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1835 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1839 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1840 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1842 // Grey out message if bitcoin address
\r
1843 bool fEnable = !fBitcoinAddress;
\r
1844 m_staticTextFrom->Enable(fEnable);
\r
1845 m_textCtrlFrom->Enable(fEnable);
\r
1846 m_staticTextMessage->Enable(fEnable);
\r
1847 m_textCtrlMessage->Enable(fEnable);
\r
1848 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1849 if (!fEnable && fEnabledPrev)
\r
1851 strFromSave = m_textCtrlFrom->GetValue();
\r
1852 strMessageSave = m_textCtrlMessage->GetValue();
\r
1853 m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
\r
1854 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
\r
1856 else if (fEnable && !fEnabledPrev)
\r
1858 m_textCtrlFrom->SetValue(strFromSave);
\r
1859 m_textCtrlMessage->SetValue(strMessageSave);
\r
1861 fEnabledPrev = fEnable;
\r
1864 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1866 // Reformat the amount
\r
1868 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1871 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1872 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1875 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1877 // Open address book
\r
1878 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
\r
1879 if (dialog.ShowModal())
\r
1880 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
\r
1883 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1885 // Copy clipboard to address box
\r
1886 if (wxTheClipboard->Open())
\r
1888 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1890 wxTextDataObject data;
\r
1891 wxTheClipboard->GetData(data);
\r
1892 m_textCtrlAddress->SetValue(data.GetText());
\r
1894 wxTheClipboard->Close();
\r
1898 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1901 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1905 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1907 wxMessageBox(_("Error in amount "), _("Send Coins"));
\r
1910 if (nValue > GetBalance())
\r
1912 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
\r
1915 if (nValue + nTransactionFee > GetBalance())
\r
1917 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
\r
1921 // Parse bitcoin address
\r
1923 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1925 if (fBitcoinAddress)
\r
1927 // Send to bitcoin address
\r
1928 CScript scriptPubKey;
\r
1929 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1931 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
\r
1932 if (strError == "")
\r
1933 wxMessageBox(_("Payment sent "), _("Sending..."));
\r
1934 else if (strError != "ABORTED")
\r
1935 wxMessageBox(strError + " ", _("Sending..."));
\r
1939 // Parse IP address
\r
1940 CAddress addr(strAddress);
\r
1941 if (!addr.IsValid())
\r
1943 wxMessageBox(_("Invalid address "), _("Send Coins"));
\r
1948 wtx.mapValue["to"] = strAddress;
\r
1949 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1950 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1952 // Send to IP address
\r
1953 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1954 if (!pdialog->ShowModal())
\r
1958 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1959 if (!mapAddressBook.count(strAddress))
\r
1960 SetAddressBookName(strAddress, "");
\r
1965 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1976 //////////////////////////////////////////////////////////////////////////////
\r
1981 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
1984 nPrice = nPriceIn;
\r
1986 start = wxDateTime::UNow();
\r
1987 memset(pszStatus, 0, sizeof(pszStatus));
\r
1988 fCanCancel = true;
\r
1992 fWorkDone = false;
\r
1994 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
\r
1997 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1998 m_textCtrlStatus->SetValue("");
\r
2000 CreateThread(SendingDialogStartTransfer, this);
\r
2003 CSendingDialog::~CSendingDialog()
\r
2005 printf("~CSendingDialog()\n");
\r
2008 void CSendingDialog::Close()
\r
2010 // Last one out turn out the lights.
\r
2011 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
2012 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
2013 // This allows the window to disappear and end modality when cancelled
\r
2014 // without making the user wait for ConnectNode to return. The dialog object
\r
2015 // hangs around in the background until the work thread exits.
\r
2017 EndModal(fSuccess);
\r
2026 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
2028 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
2035 wxCommandEvent cmdevent;
\r
2036 OnButtonCancel(cmdevent);
\r
2040 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
2046 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
2052 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
2055 if (strlen(pszStatus) > 130)
\r
2056 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
2058 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
2059 m_staticTextSending->SetFocus();
\r
2061 m_buttonCancel->Enable(false);
\r
2064 m_buttonOK->Enable(true);
\r
2065 m_buttonOK->SetFocus();
\r
2066 m_buttonCancel->Enable(false);
\r
2068 if (fAbort && fCanCancel && IsShown())
\r
2070 strcpy(pszStatus, _("CANCELLED"));
\r
2071 m_buttonOK->Enable(true);
\r
2072 m_buttonOK->SetFocus();
\r
2073 m_buttonCancel->Enable(false);
\r
2074 m_buttonCancel->SetLabel(_("Cancelled"));
\r
2076 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
\r
2082 // Everything from here on is not in the UI thread and must only communicate
\r
2083 // with the rest of the dialog through variables and calling repaint.
\r
2086 void CSendingDialog::Repaint()
\r
2089 wxPaintEvent event;
\r
2090 GetEventHandler()->AddPendingEvent(event);
\r
2093 bool CSendingDialog::Status()
\r
2100 if (fAbort && fCanCancel)
\r
2102 memset(pszStatus, 0, 10);
\r
2103 strcpy(pszStatus, _("CANCELLED"));
\r
2111 bool CSendingDialog::Status(const string& str)
\r
2116 // This can be read by the UI thread at any time,
\r
2117 // so copy in a way that can be read cleanly at all times.
\r
2118 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
2119 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
2125 bool CSendingDialog::Error(const string& str)
\r
2127 fCanCancel = false;
\r
2129 Status(string(_("Error: ")) + str);
\r
2133 void SendingDialogStartTransfer(void* parg)
\r
2135 ((CSendingDialog*)parg)->StartTransfer();
\r
2138 void CSendingDialog::StartTransfer()
\r
2140 // Make sure we have enough money
\r
2141 if (nPrice + nTransactionFee > GetBalance())
\r
2143 Error(_("Insufficient funds"));
\r
2147 // We may have connected already for product details
\r
2148 if (!Status(_("Connecting...")))
\r
2150 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
2153 Error(_("Unable to connect"));
\r
2157 // Send order to seller, with response going to OnReply2 via event handler
\r
2158 if (!Status(_("Requesting public key...")))
\r
2160 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
2163 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
2165 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2168 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2170 if (!Status(_("Received public key...")))
\r
2173 CScript scriptPubKey;
\r
2180 string strMessage;
\r
2181 vRecv >> strMessage;
\r
2182 Error(_("Transfer was not accepted"));
\r
2183 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2186 vRecv >> scriptPubKey;
\r
2190 //// what do we want to do about this?
\r
2191 Error(_("Invalid response received"));
\r
2195 // Pause to give the user a chance to cancel
\r
2196 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2203 CRITICAL_BLOCK(cs_main)
\r
2206 if (!Status(_("Creating transaction...")))
\r
2208 if (nPrice + nTransactionFee > GetBalance())
\r
2210 Error(_("Insufficient funds"));
\r
2214 int64 nFeeRequired;
\r
2215 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2217 if (nPrice + nFeeRequired > GetBalance())
\r
2218 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
\r
2220 Error(_("Transaction creation failed"));
\r
2224 // Transaction fee
\r
2225 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
\r
2227 Error(_("Transaction aborted"));
\r
2231 // Make sure we're still connected
\r
2232 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2235 Error(_("Lost connection, transaction cancelled"));
\r
2239 // Last chance to cancel
\r
2243 fCanCancel = false;
\r
2246 fCanCancel = true;
\r
2249 fCanCancel = false;
\r
2251 if (!Status(_("Sending payment...")))
\r
2255 if (!CommitTransaction(wtx, key))
\r
2257 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
2261 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2262 CWalletTx wtxSend = wtx;
\r
2263 wtxSend.fFromMe = false;
\r
2264 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
\r
2266 Status(_("Waiting for confirmation..."));
\r
2267 MainFrameRepaint();
\r
2271 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2273 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2276 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2284 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
\r
2285 "The transaction is recorded and will credit to the recipient,\n"
\r
2286 "but the comment information will be blank."));
\r
2292 //// what do we want to do about this?
\r
2293 Error(_("Payment was sent, but an invalid response was received"));
\r
2299 Status(_("Payment completed"));
\r
2307 //////////////////////////////////////////////////////////////////////////////
\r
2309 // CAddressBookDialog
\r
2312 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
\r
2314 // Set initially selected page
\r
2315 wxNotebookEvent event;
\r
2316 event.SetSelection(nPageIn);
\r
2317 OnNotebookPageChanged(event);
\r
2318 m_notebook->ChangeSelection(nPageIn);
\r
2320 fDuringSend = fDuringSendIn;
\r
2322 m_buttonCancel->Show(false);
\r
2325 wxIcon iconAddressBook;
\r
2326 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2327 SetIcon(iconAddressBook);
\r
2329 // Init column headers
\r
2330 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
\r
2331 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
\r
2332 m_listCtrlSending->SetFocus();
\r
2333 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
\r
2334 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
\r
2335 m_listCtrlReceiving->SetFocus();
\r
2337 // Fill listctrl with address book data
\r
2338 CRITICAL_BLOCK(cs_mapKeys)
\r
2339 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2341 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
\r
2342 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2344 string strAddress = item.first;
\r
2345 string strName = item.second;
\r
2347 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2348 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
\r
2349 int nIndex = InsertLine(plistCtrl, strName, strAddress);
\r
2350 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
\r
2351 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2356 wxString CAddressBookDialog::GetSelectedAddress()
\r
2358 int nIndex = GetSelection(m_listCtrl);
\r
2361 return GetItemText(m_listCtrl, nIndex, 1);
\r
2364 wxString CAddressBookDialog::GetSelectedSendingAddress()
\r
2366 int nIndex = GetSelection(m_listCtrlSending);
\r
2369 return GetItemText(m_listCtrlSending, nIndex, 1);
\r
2372 wxString CAddressBookDialog::GetSelectedReceivingAddress()
\r
2374 int nIndex = GetSelection(m_listCtrlReceiving);
\r
2377 return GetItemText(m_listCtrlReceiving, nIndex, 1);
\r
2380 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
\r
2383 nPage = event.GetSelection();
\r
2384 if (nPage == SENDING)
\r
2385 m_listCtrl = m_listCtrlSending;
\r
2386 else if (nPage == RECEIVING)
\r
2387 m_listCtrl = m_listCtrlReceiving;
\r
2388 m_buttonDelete->Show(nPage == SENDING);
\r
2389 m_buttonCopy->Show(nPage == RECEIVING);
\r
2391 m_listCtrl->SetFocus();
\r
2394 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2396 // Update address book with edited name
\r
2398 if (event.IsEditCancelled())
\r
2400 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2401 SetAddressBookName(strAddress, string(event.GetText()));
\r
2402 pframeMain->RefreshListCtrl();
\r
2405 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2408 if (nPage == RECEIVING)
\r
2409 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
\r
2412 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2417 // Doubleclick returns selection
\r
2418 EndModal(GetSelectedAddress() != "" ? 2 : 0);
\r
2422 // Doubleclick edits item
\r
2423 wxCommandEvent event2;
\r
2424 OnButtonEdit(event2);
\r
2427 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2429 if (nPage != SENDING)
\r
2431 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2433 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2435 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2436 CWalletDB().EraseName(strAddress);
\r
2437 m_listCtrl->DeleteItem(nIndex);
\r
2440 pframeMain->RefreshListCtrl();
\r
2443 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2445 // Copy address box to clipboard
\r
2446 if (wxTheClipboard->Open())
\r
2448 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
\r
2449 wxTheClipboard->Close();
\r
2453 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2456 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2458 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
\r
2462 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2464 int nIndex = GetSelection(m_listCtrl);
\r
2467 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2468 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2469 string strAddressOrg = strAddress;
\r
2471 if (nPage == SENDING)
\r
2473 // Ask name and address
\r
2476 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
\r
2477 if (!dialog.ShowModal())
\r
2479 strName = dialog.GetValue1();
\r
2480 strAddress = dialog.GetValue2();
\r
2482 while (CheckIfMine(strAddress, _("Edit Address")));
\r
2485 else if (nPage == RECEIVING)
\r
2488 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
\r
2489 if (!dialog.ShowModal())
\r
2491 strName = dialog.GetValue();
\r
2495 if (strAddress != strAddressOrg)
\r
2496 CWalletDB().EraseName(strAddressOrg);
\r
2497 SetAddressBookName(strAddress, strName);
\r
2498 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2499 m_listCtrl->SetItemText(nIndex, strName);
\r
2500 pframeMain->RefreshListCtrl();
\r
2503 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2506 string strAddress;
\r
2508 if (nPage == SENDING)
\r
2510 // Ask name and address
\r
2513 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
\r
2514 if (!dialog.ShowModal())
\r
2516 strName = dialog.GetValue1();
\r
2517 strAddress = dialog.GetValue2();
\r
2519 while (CheckIfMine(strAddress, _("Add Address")));
\r
2521 else if (nPage == RECEIVING)
\r
2524 CGetTextFromUserDialog dialog(this,
\r
2525 _("New Receiving Address"),
\r
2526 _("You should use a new address for each payment you receive.\n\nLabel"),
\r
2528 if (!dialog.ShowModal())
\r
2530 strName = dialog.GetValue();
\r
2532 // Generate new key
\r
2533 strAddress = PubKeyToAddress(GenerateNewKey());
\r
2536 // Add to list and select it
\r
2537 SetAddressBookName(strAddress, strName);
\r
2538 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2539 SetSelection(m_listCtrl, nIndex);
\r
2540 m_listCtrl->SetFocus();
\r
2541 if (nPage == SENDING)
\r
2542 pframeMain->RefreshListCtrl();
\r
2545 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2548 EndModal(GetSelectedAddress() != "" ? 1 : 0);
\r
2551 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2557 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2568 //////////////////////////////////////////////////////////////////////////////
\r
2575 ID_TASKBAR_RESTORE = 10001,
\r
2576 ID_TASKBAR_OPTIONS,
\r
2577 ID_TASKBAR_GENERATE,
\r
2581 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
2582 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
2583 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
2584 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
2585 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
2586 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
2587 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
2590 void CMyTaskBarIcon::Show(bool fShow)
\r
2592 static char pszPrevTip[200];
\r
2595 string strTooltip = _("Bitcoin");
\r
2596 if (fGenerateBitcoins)
\r
2597 strTooltip = _("Bitcoin - Generating");
\r
2598 if (fGenerateBitcoins && vNodes.empty())
\r
2599 strTooltip = _("Bitcoin - (not connected)");
\r
2601 // Optimization, only update when changed, using char array to be reentrant
\r
2602 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
2604 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
2606 // somehow it'll choose the wrong size and scale it down if
\r
2607 // we use the main icon, so we hand it one with only 16x16
\r
2608 SetIcon(wxICON(favicon), strTooltip);
\r
2610 SetIcon(bitcoin80_xpm, strTooltip);
\r
2616 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
2621 void CMyTaskBarIcon::Hide()
\r
2626 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
2631 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
2636 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
2638 // Since it's modal, get the main window to do it
\r
2639 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
\r
2640 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
2643 void CMyTaskBarIcon::Restore()
\r
2645 pframeMain->Show();
\r
2646 wxIconizeEvent event(0, false);
\r
2647 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
2648 pframeMain->Iconize(false);
\r
2649 pframeMain->Raise();
\r
2652 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
2654 GenerateBitcoins(event.IsChecked());
\r
2657 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
2659 event.Check(fGenerateBitcoins);
\r
2662 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
2664 pframeMain->Close(true);
\r
2667 void CMyTaskBarIcon::UpdateTooltip()
\r
2669 if (IsIconInstalled())
\r
2673 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
2675 wxMenu* pmenu = new wxMenu;
\r
2676 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
\r
2677 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
\r
2678 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
\r
2679 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
2680 pmenu->AppendSeparator();
\r
2681 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
\r
2691 //////////////////////////////////////////////////////////////////////////////
\r
2696 void CreateMainWindow()
\r
2698 pframeMain = new CMainFrame(NULL);
\r
2699 if (mapArgs.count("-min"))
\r
2700 pframeMain->Iconize(true);
\r
2702 if (!mapArgs.count("-minimizetotray"))
\r
2703 fMinimizeToTray = false;
\r
2705 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
2706 if (fMinimizeToTray && pframeMain->IsIconized())
\r
2707 fClosedToTray = true;
\r
2708 pframeMain->Show(!fClosedToTray);
\r
2709 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
2710 CreateThread(ThreadDelayedRepaint, NULL);
\r
2714 // Define a new application
\r
2715 class CMyApp : public wxApp
\r
2724 // Hook Initialize so we can start without GUI
\r
2725 virtual bool Initialize(int& argc, wxChar** argv);
\r
2727 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
2728 // event handler here
\r
2729 virtual bool OnExceptionInMainLoop();
\r
2731 // 3rd, and final, level exception handling: whenever an unhandled
\r
2732 // exception is caught, this function is called
\r
2733 virtual void OnUnhandledException();
\r
2735 // and now for something different: this function is called in case of a
\r
2736 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
2737 virtual void OnFatalException();
\r
2740 IMPLEMENT_APP(CMyApp)
\r
2742 bool CMyApp::Initialize(int& argc, wxChar** argv)
\r
2744 for (int i = 1; i < argc; i++)
\r
2745 if (!IsSwitchChar(argv[i][0]))
\r
2746 fCommandLine = true;
\r
2748 if (!fCommandLine)
\r
2750 // wxApp::Initialize will remove environment-specific parameters,
\r
2751 // so it's too early to call ParseParameters yet
\r
2752 for (int i = 1; i < argc; i++)
\r
2754 wxString str = argv[i];
\r
2756 if (str.size() >= 1 && str[0] == '/')
\r
2758 char pszLower[MAX_PATH];
\r
2759 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
\r
2763 if (str == "-daemon")
\r
2769 if (fDaemon || fCommandLine)
\r
2771 // Call the original Initialize while suppressing error messages
\r
2772 // and ignoring failure. If unable to initialize GTK, it fails
\r
2773 // near the end so hopefully the last few things don't matter.
\r
2776 wxApp::Initialize(argc, argv);
\r
2782 pid_t pid = fork();
\r
2785 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
\r
2789 pthread_exit((void*)0);
\r
2796 return wxApp::Initialize(argc, argv);
\r
2799 bool CMyApp::OnInit()
\r
2801 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
\r
2802 // Disable malfunctioning wxWidgets debug assertion
\r
2803 extern int g_isPainting;
\r
2804 g_isPainting = 10000;
\r
2807 wxImage::AddHandler(new wxPNGHandler);
\r
2809 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
\r
2810 SetAppName("Bitcoin");
\r
2812 SetAppName("bitcoin");
\r
2816 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
\r
2817 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
\r
2818 class wxMBConv_win32 : public wxMBConv
\r
2822 size_t m_minMBCharWidth;
\r
2824 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
\r
2825 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
\r
2829 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
\r
2830 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
\r
2831 g_locale.AddCatalogLookupPathPrefix("locale");
\r
2833 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
\r
2834 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
\r
2836 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
\r
2837 g_locale.AddCatalog("bitcoin");
\r
2839 return AppInit(argc, argv);
\r
2842 int CMyApp::OnExit()
\r
2845 return wxApp::OnExit();
\r
2848 bool CMyApp::OnExceptionInMainLoop()
\r
2854 catch (std::exception& e)
\r
2856 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
2857 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2863 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
2864 wxLogWarning("Unknown exception");
\r
2871 void CMyApp::OnUnhandledException()
\r
2873 // this shows how we may let some exception propagate uncaught
\r
2878 catch (std::exception& e)
\r
2880 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
2881 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2887 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
2888 wxLogWarning("Unknown exception");
\r
2894 void CMyApp::OnFatalException()
\r
2896 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);
\r