Bugfix: avoid sub-cent change (lost in fees) whenever possible
[bitcoin.git] / ui.cpp
blob17ad630832e46f3bb0037e26c5dfeeb366762379
1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
5 #include "headers.h"
6 #ifdef _MSC_VER
7 #include <crtdbg.h>
8 #endif
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
14 CMainFrame* pframeMain = NULL;
15 CMyTaskBarIcon* ptaskbaricon = NULL;
16 bool fClosedToTray = false;
17 wxLocale g_locale;
27 //////////////////////////////////////////////////////////////////////////////
29 // Util
32 void HandleCtrlA(wxKeyEvent& event)
34 // Ctrl-a select all
35 event.Skip();
36 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
37 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
38 textCtrl->SetSelection(-1, -1);
41 bool Is24HourTime()
43 //char pszHourFormat[256];
44 //pszHourFormat[0] = '\0';
45 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
46 //return (pszHourFormat[0] != '0');
47 return true;
50 string DateStr(int64 nTime)
52 // Can only be used safely here in the UI
53 return (string)wxDateTime((time_t)nTime).FormatDate();
56 string DateTimeStr(int64 nTime)
58 // Can only be used safely here in the UI
59 wxDateTime datetime((time_t)nTime);
60 if (Is24HourTime())
61 return (string)datetime.Format("%x %H:%M");
62 else
63 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
66 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
68 // Helper to simplify access to listctrl
69 wxListItem item;
70 item.m_itemId = nIndex;
71 item.m_col = nColumn;
72 item.m_mask = wxLIST_MASK_TEXT;
73 if (!listCtrl->GetItem(item))
74 return "";
75 return item.GetText();
78 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
80 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
81 listCtrl->SetItem(nIndex, 1, str1);
82 return nIndex;
85 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
87 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
88 listCtrl->SetItem(nIndex, 1, str1);
89 listCtrl->SetItem(nIndex, 2, str2);
90 listCtrl->SetItem(nIndex, 3, str3);
91 listCtrl->SetItem(nIndex, 4, str4);
92 return nIndex;
95 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
97 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
98 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
99 listCtrl->SetItem(nIndex, 1, str1);
100 listCtrl->SetItem(nIndex, 2, str2);
101 listCtrl->SetItem(nIndex, 3, str3);
102 listCtrl->SetItem(nIndex, 4, str4);
103 return nIndex;
106 void SetItemTextColour(wxListCtrl* listCtrl, int nIndex, const wxColour& colour)
108 // Repaint on Windows is more flickery if the colour has ever been set,
109 // so don't want to set it unless it's different. Default colour has
110 // alpha 0 transparent, so our colours don't match using operator==.
111 wxColour c1 = listCtrl->GetItemTextColour(nIndex);
112 if (!c1.IsOk())
113 c1 = wxColour(0,0,0);
114 if (colour.Red() != c1.Red() || colour.Green() != c1.Green() || colour.Blue() != c1.Blue())
115 listCtrl->SetItemTextColour(nIndex, colour);
118 void SetSelection(wxListCtrl* listCtrl, int nIndex)
120 int nSize = listCtrl->GetItemCount();
121 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
122 for (int i = 0; i < nSize; i++)
123 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
126 int GetSelection(wxListCtrl* listCtrl)
128 int nSize = listCtrl->GetItemCount();
129 for (int i = 0; i < nSize; i++)
130 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
131 return i;
132 return -1;
135 string HtmlEscape(const char* psz, bool fMultiLine=false)
137 int len = 0;
138 for (const char* p = psz; *p; p++)
140 if (*p == '<') len += 4;
141 else if (*p == '>') len += 4;
142 else if (*p == '&') len += 5;
143 else if (*p == '"') len += 6;
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
145 else if (*p == '\n' && fMultiLine) len += 5;
146 else
147 len++;
149 string str;
150 str.reserve(len);
151 for (const char* p = psz; *p; p++)
153 if (*p == '<') str += "&lt;";
154 else if (*p == '>') str += "&gt;";
155 else if (*p == '&') str += "&amp;";
156 else if (*p == '"') str += "&quot;";
157 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += "&nbsp;";
158 else if (*p == '\n' && fMultiLine) str += "<br>\n";
159 else
160 str += *p;
162 return str;
165 string HtmlEscape(const string& str, bool fMultiLine=false)
167 return HtmlEscape(str.c_str(), fMultiLine);
170 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
172 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
173 *pfDone = true;
176 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
178 #ifdef __WXMSW__
179 return wxMessageBox(message, caption, style, parent, x, y);
180 #else
181 if (wxThread::IsMain() || fDaemon)
183 return wxMessageBox(message, caption, style, parent, x, y);
185 else
187 int nRet = 0;
188 bool fDone = false;
189 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
190 while (!fDone)
191 Sleep(100);
192 return nRet;
194 #endif
197 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
199 if (nFeeRequired < CENT || nFeeRequired <= nTransactionFee || fDaemon)
200 return true;
201 string strMessage = strprintf(
202 _("This transaction is over the size limit. You can still send it for a fee of %s, "
203 "which goes to the nodes that process your transaction and helps to support the network. "
204 "Do you want to pay the fee?"),
205 FormatMoney(nFeeRequired).c_str());
206 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
209 void CalledSetStatusBar(const string& strText, int nField)
211 if (nField == 0 && GetWarnings("statusbar") != "")
212 return;
213 if (pframeMain && pframeMain->m_statusBar)
214 pframeMain->m_statusBar->SetStatusText(strText, nField);
217 void SetDefaultReceivingAddress(const string& strAddress)
219 // Update main window address and database
220 if (pframeMain == NULL)
221 return;
222 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
224 uint160 hash160;
225 if (!AddressToHash160(strAddress, hash160))
226 return;
227 if (!mapPubKeys.count(hash160))
228 return;
229 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
230 pframeMain->m_textCtrlAddress->SetValue(strAddress);
243 //////////////////////////////////////////////////////////////////////////////
245 // CMainFrame
248 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
250 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
252 // Set initially selected page
253 wxNotebookEvent event;
254 event.SetSelection(0);
255 OnNotebookPageChanged(event);
256 m_notebook->ChangeSelection(0);
258 // Init
259 fRefreshListCtrl = false;
260 fRefreshListCtrlRunning = false;
261 fOnSetFocusAddress = false;
262 fRefresh = false;
263 m_choiceFilter->SetSelection(0);
264 double dResize = 1.0;
265 #ifdef __WXMSW__
266 SetIcon(wxICON(bitcoin));
267 #else
268 SetIcon(bitcoin80_xpm);
269 SetBackgroundColour(m_toolBar->GetBackgroundColour());
270 wxFont fontTmp = m_staticText41->GetFont();
271 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
272 m_staticTextBalance->SetFont(fontTmp);
273 m_staticTextBalance->SetSize(140, 17);
274 // resize to fit ubuntu's huge default font
275 dResize = 1.22;
276 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
277 #endif
278 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
279 m_listCtrl->SetFocus();
280 ptaskbaricon = new CMyTaskBarIcon();
281 #ifdef __WXMAC_OSX__
282 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
283 // to their standard places, leaving these menus empty.
284 GetMenuBar()->Remove(2); // remove Help menu
285 GetMenuBar()->Remove(0); // remove File menu
286 #endif
288 // Init column headers
289 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
290 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
291 nDateWidth += 12;
292 #ifdef __WXMAC_OSX__
293 nDateWidth += 5;
294 dResize -= 0.01;
295 #endif
296 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
297 foreach(wxListCtrl* p, pplistCtrl)
299 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
300 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
301 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
302 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
303 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
304 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
305 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
308 // Init status bar
309 int pnWidths[3] = { -100, 88, 300 };
310 #ifndef __WXMSW__
311 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
312 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
313 #endif
314 m_statusBar->SetFieldsCount(3, pnWidths);
316 // Fill your address text box
317 vector<unsigned char> vchPubKey;
318 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
319 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
321 // Fill listctrl with wallet transactions
322 RefreshListCtrl();
325 CMainFrame::~CMainFrame()
327 pframeMain = NULL;
328 delete ptaskbaricon;
329 ptaskbaricon = NULL;
332 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
334 event.Skip();
335 nPage = event.GetSelection();
336 if (nPage == ALL)
338 m_listCtrl = m_listCtrlAll;
339 fShowGenerated = true;
340 fShowSent = true;
341 fShowReceived = true;
343 else if (nPage == SENTRECEIVED)
345 m_listCtrl = m_listCtrlSentReceived;
346 fShowGenerated = false;
347 fShowSent = true;
348 fShowReceived = true;
350 else if (nPage == SENT)
352 m_listCtrl = m_listCtrlSent;
353 fShowGenerated = false;
354 fShowSent = true;
355 fShowReceived = false;
357 else if (nPage == RECEIVED)
359 m_listCtrl = m_listCtrlReceived;
360 fShowGenerated = false;
361 fShowSent = false;
362 fShowReceived = true;
364 RefreshListCtrl();
365 m_listCtrl->SetFocus();
368 void CMainFrame::OnClose(wxCloseEvent& event)
370 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
372 // Divert close to minimize
373 event.Veto();
374 fClosedToTray = true;
375 Iconize(true);
377 else
379 Destroy();
380 CreateThread(Shutdown, NULL);
384 void CMainFrame::OnIconize(wxIconizeEvent& event)
386 event.Skip();
387 // Hide the task bar button when minimized.
388 // Event is sent when the frame is minimized or restored.
389 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
390 // to get rid of the deprecated warning. Just ignore it.
391 if (!event.Iconized())
392 fClosedToTray = false;
393 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
394 if (GetBoolArg("-minimizetotray")) {
395 #endif
396 // The tray icon sometimes disappears on ubuntu karmic
397 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
398 // Reports of CPU peg on 64-bit linux
399 if (fMinimizeToTray && event.Iconized())
400 fClosedToTray = true;
401 Show(!fClosedToTray);
402 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
403 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
405 #endif
408 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
410 event.Skip();
411 RandAddSeed();
412 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
413 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
416 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
418 // Hidden columns not resizeable
419 if (event.GetColumn() <= 1 && !fDebug)
420 event.Veto();
421 else
422 event.Skip();
425 int CMainFrame::GetSortIndex(const string& strSort)
427 #ifdef __WXMSW__
428 return 0;
429 #else
430 // The wx generic listctrl implementation used on GTK doesn't sort,
431 // so we have to do it ourselves. Remember, we sort in reverse order.
432 // In the wx generic implementation, they store the list of items
433 // in a vector, so indexed lookups are fast, but inserts are slower
434 // the closer they are to the top.
435 int low = 0;
436 int high = m_listCtrl->GetItemCount();
437 while (low < high)
439 int mid = low + ((high - low) / 2);
440 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
441 high = mid;
442 else
443 low = mid + 1;
445 return low;
446 #endif
449 void CMainFrame::InsertLine(bool fNew, int nIndex, uint256 hashKey, string strSort, const wxColour& colour, const wxString& str2, const wxString& str3, const wxString& str4, const wxString& str5, const wxString& str6)
451 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
452 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
454 // Find item
455 if (!fNew && nIndex == -1)
457 string strHash = " " + hashKey.ToString();
458 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
459 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
460 break;
463 // fNew is for blind insert, only use if you're sure it's new
464 if (fNew || nIndex == -1)
466 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
468 else
470 // If sort key changed, must delete and reinsert to make it relocate
471 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
473 m_listCtrl->DeleteItem(nIndex);
474 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
478 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
479 m_listCtrl->SetItem(nIndex, 2, str2);
480 m_listCtrl->SetItem(nIndex, 3, str3);
481 m_listCtrl->SetItem(nIndex, 4, str4);
482 m_listCtrl->SetItem(nIndex, 5, str5);
483 m_listCtrl->SetItem(nIndex, 6, str6);
484 m_listCtrl->SetItemData(nIndex, nData);
485 SetItemTextColour(m_listCtrl, nIndex, colour);
488 bool CMainFrame::DeleteLine(uint256 hashKey)
490 long nData = *(long*)&hashKey;
492 // Find item
493 int nIndex = -1;
494 string strHash = " " + hashKey.ToString();
495 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
496 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
497 break;
499 if (nIndex != -1)
500 m_listCtrl->DeleteItem(nIndex);
502 return nIndex != -1;
505 string FormatTxStatus(const CWalletTx& wtx)
507 // Status
508 if (!wtx.IsFinal())
510 if (wtx.nLockTime < 500000000)
511 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
512 else
513 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
515 else
517 int nDepth = wtx.GetDepthInMainChain();
518 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
519 return strprintf(_("%d/offline?"), nDepth);
520 else if (nDepth < 6)
521 return strprintf(_("%d/unconfirmed"), nDepth);
522 else
523 return strprintf(_("%d confirmations"), nDepth);
527 string SingleLine(const string& strIn)
529 string strOut;
530 bool fOneSpace = false;
531 foreach(unsigned char c, strIn)
533 if (isspace(c))
535 fOneSpace = true;
537 else if (c > ' ')
539 if (fOneSpace && !strOut.empty())
540 strOut += ' ';
541 strOut += c;
542 fOneSpace = false;
545 return strOut;
548 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
550 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
551 int64 nCredit = wtx.GetCredit(true);
552 int64 nDebit = wtx.GetDebit();
553 int64 nNet = nCredit - nDebit;
554 uint256 hash = wtx.GetHash();
555 string strStatus = FormatTxStatus(wtx);
556 bool fConfirmed = wtx.fConfirmedDisplayed = wtx.IsConfirmed();
557 wxColour colour = (fConfirmed ? wxColour(0,0,0) : wxColour(128,128,128));
558 map<string, string> mapValue = wtx.mapValue;
559 wtx.nLinesDisplayed = 1;
560 nListViewUpdated++;
562 // Filter
563 if (wtx.IsCoinBase())
565 // Don't show generated coin until confirmed by at least one block after it
566 // so we don't get the user's hopes up until it looks like it's probably accepted.
568 // It is not an error when generated blocks are not accepted. By design,
569 // some percentage of blocks, like 10% or more, will end up not accepted.
570 // This is the normal mechanism by which the network copes with latency.
572 // We display regular transactions right away before any confirmation
573 // because they can always get into some block eventually. Generated coins
574 // are special because if their block is not accepted, they are not valid.
576 if (wtx.GetDepthInMainChain() < 2)
578 wtx.nLinesDisplayed = 0;
579 return false;
582 if (!fShowGenerated)
583 return false;
586 // Find the block the tx is in
587 CBlockIndex* pindex = NULL;
588 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
589 if (mi != mapBlockIndex.end())
590 pindex = (*mi).second;
592 // Sort order, unrecorded transactions sort to the top
593 string strSort = strprintf("%010d-%01d-%010u",
594 (pindex ? pindex->nHeight : INT_MAX),
595 (wtx.IsCoinBase() ? 1 : 0),
596 wtx.nTimeReceived);
598 // Insert line
599 if (nNet > 0 || wtx.IsCoinBase())
602 // Credit
604 string strDescription;
605 if (wtx.IsCoinBase())
607 // Generated
608 strDescription = _("Generated");
609 if (nCredit == 0)
611 int64 nUnmatured = 0;
612 foreach(const CTxOut& txout, wtx.vout)
613 nUnmatured += txout.GetCredit();
614 if (wtx.IsInMainChain())
616 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
618 // Check if the block was requested by anyone
619 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
620 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
622 else
624 strDescription = _("Generated (not accepted)");
628 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
630 // Received by IP connection
631 if (!fShowReceived)
632 return false;
633 if (!mapValue["from"].empty())
634 strDescription += _("From: ") + mapValue["from"];
635 if (!mapValue["message"].empty())
637 if (!strDescription.empty())
638 strDescription += " - ";
639 strDescription += mapValue["message"];
642 else
644 // Received by Bitcoin Address
645 if (!fShowReceived)
646 return false;
647 foreach(const CTxOut& txout, wtx.vout)
649 if (txout.IsMine())
651 vector<unsigned char> vchPubKey;
652 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
654 CRITICAL_BLOCK(cs_mapAddressBook)
656 //strDescription += _("Received payment to ");
657 //strDescription += _("Received with address ");
658 strDescription += _("Received with: ");
659 string strAddress = PubKeyToAddress(vchPubKey);
660 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
661 if (mi != mapAddressBook.end() && !(*mi).second.empty())
663 string strLabel = (*mi).second;
664 strDescription += strAddress.substr(0,12) + "... ";
665 strDescription += "(" + strLabel + ")";
667 else
668 strDescription += strAddress;
671 break;
676 string strCredit = FormatMoney(nNet, true);
677 if (!fConfirmed)
678 strCredit = "[" + strCredit + "]";
680 InsertLine(fNew, nIndex, hash, strSort, colour,
681 strStatus,
682 nTime ? DateTimeStr(nTime) : "",
683 SingleLine(strDescription),
685 strCredit);
687 else
689 bool fAllFromMe = true;
690 foreach(const CTxIn& txin, wtx.vin)
691 fAllFromMe = fAllFromMe && txin.IsMine();
693 bool fAllToMe = true;
694 foreach(const CTxOut& txout, wtx.vout)
695 fAllToMe = fAllToMe && txout.IsMine();
697 if (fAllFromMe && fAllToMe)
699 // Payment to self
700 int64 nChange = wtx.GetChange();
701 InsertLine(fNew, nIndex, hash, strSort, colour,
702 strStatus,
703 nTime ? DateTimeStr(nTime) : "",
704 _("Payment to yourself"),
705 FormatMoney(-(nDebit - nChange), true),
706 FormatMoney(nCredit - nChange, true));
708 else if (fAllFromMe)
711 // Debit
713 if (!fShowSent)
714 return false;
716 int64 nTxFee = nDebit - wtx.GetValueOut();
717 wtx.nLinesDisplayed = 0;
718 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
720 const CTxOut& txout = wtx.vout[nOut];
721 if (txout.IsMine())
722 continue;
724 string strAddress;
725 if (!mapValue["to"].empty())
727 // Sent to IP
728 strAddress = mapValue["to"];
730 else
732 // Sent to Bitcoin Address
733 uint160 hash160;
734 if (ExtractHash160(txout.scriptPubKey, hash160))
735 strAddress = Hash160ToAddress(hash160);
738 string strDescription = _("To: ");
739 CRITICAL_BLOCK(cs_mapAddressBook)
740 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
741 strDescription += mapAddressBook[strAddress] + " ";
742 strDescription += strAddress;
743 if (!mapValue["message"].empty())
745 if (!strDescription.empty())
746 strDescription += " - ";
747 strDescription += mapValue["message"];
749 else if (!mapValue["comment"].empty())
751 if (!strDescription.empty())
752 strDescription += " - ";
753 strDescription += mapValue["comment"];
756 int64 nValue = txout.nValue;
757 if (nTxFee > 0)
759 nValue += nTxFee;
760 nTxFee = 0;
763 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut), colour,
764 strStatus,
765 nTime ? DateTimeStr(nTime) : "",
766 SingleLine(strDescription),
767 FormatMoney(-nValue, true),
768 "");
769 nIndex = -1;
770 wtx.nLinesDisplayed++;
773 else
776 // Mixed debit transaction, can't break down payees
778 bool fAllMine = true;
779 foreach(const CTxOut& txout, wtx.vout)
780 fAllMine = fAllMine && txout.IsMine();
781 foreach(const CTxIn& txin, wtx.vin)
782 fAllMine = fAllMine && txin.IsMine();
784 InsertLine(fNew, nIndex, hash, strSort, colour,
785 strStatus,
786 nTime ? DateTimeStr(nTime) : "",
788 FormatMoney(nNet, true),
789 "");
793 return true;
796 void CMainFrame::RefreshListCtrl()
798 fRefreshListCtrl = true;
799 ::wxWakeUpIdle();
802 void CMainFrame::OnIdle(wxIdleEvent& event)
804 if (fRefreshListCtrl)
806 // Collect list of wallet transactions and sort newest first
807 bool fEntered = false;
808 vector<pair<unsigned int, uint256> > vSorted;
809 TRY_CRITICAL_BLOCK(cs_mapWallet)
811 printf("RefreshListCtrl starting\n");
812 fEntered = true;
813 fRefreshListCtrl = false;
814 vWalletUpdated.clear();
816 // Do the newest transactions first
817 vSorted.reserve(mapWallet.size());
818 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
820 const CWalletTx& wtx = (*it).second;
821 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
822 vSorted.push_back(make_pair(nTime, (*it).first));
824 m_listCtrl->DeleteAllItems();
826 if (!fEntered)
827 return;
829 sort(vSorted.begin(), vSorted.end());
831 // Fill list control
832 for (int i = 0; i < vSorted.size();)
834 if (fShutdown)
835 return;
836 bool fEntered = false;
837 TRY_CRITICAL_BLOCK(cs_mapWallet)
839 fEntered = true;
840 uint256& hash = vSorted[i++].second;
841 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
842 if (mi != mapWallet.end())
843 InsertTransaction((*mi).second, true);
845 if (!fEntered || i == 100 || i % 500 == 0)
846 wxYield();
849 printf("RefreshListCtrl done\n");
851 // Update transaction total display
852 MainFrameRepaint();
854 else
856 // Check for time updates
857 static int64 nLastTime;
858 if (GetTime() > nLastTime + 30)
860 TRY_CRITICAL_BLOCK(cs_mapWallet)
862 nLastTime = GetTime();
863 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
865 CWalletTx& wtx = (*it).second;
866 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
867 InsertTransaction(wtx, false);
874 void CMainFrame::RefreshStatusColumn()
876 static int nLastTop;
877 static CBlockIndex* pindexLastBest;
878 static unsigned int nLastRefreshed;
880 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
881 if (nTop == nLastTop && pindexLastBest == pindexBest)
882 return;
884 TRY_CRITICAL_BLOCK(cs_mapWallet)
886 int nStart = nTop;
887 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
889 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
891 // If no updates, only need to do the part that moved onto the screen
892 if (nStart >= nLastTop && nStart < nLastTop + 100)
893 nStart = nLastTop + 100;
894 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
895 nEnd = nLastTop;
897 nLastTop = nTop;
898 pindexLastBest = pindexBest;
899 nLastRefreshed = nListViewUpdated;
901 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
903 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
904 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
905 if (mi == mapWallet.end())
907 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
908 continue;
910 CWalletTx& wtx = (*mi).second;
911 if (wtx.IsCoinBase() ||
912 wtx.GetTxTime() != wtx.nTimeDisplayed ||
913 wtx.IsConfirmed() != wtx.fConfirmedDisplayed)
915 if (!InsertTransaction(wtx, false, nIndex))
916 m_listCtrl->DeleteItem(nIndex--);
918 else
920 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
926 void CMainFrame::OnPaint(wxPaintEvent& event)
928 event.Skip();
929 if (fRefresh)
931 fRefresh = false;
932 Refresh();
937 unsigned int nNeedRepaint = 0;
938 unsigned int nLastRepaint = 0;
939 int64 nLastRepaintTime = 0;
940 int64 nRepaintInterval = 500;
942 void ThreadDelayedRepaint(void* parg)
944 while (!fShutdown)
946 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
948 nLastRepaint = nNeedRepaint;
949 if (pframeMain)
951 printf("DelayedRepaint\n");
952 wxPaintEvent event;
953 pframeMain->fRefresh = true;
954 pframeMain->GetEventHandler()->AddPendingEvent(event);
957 Sleep(nRepaintInterval);
961 void MainFrameRepaint()
963 // This is called by network code that shouldn't access pframeMain
964 // directly because it could still be running after the UI is closed.
965 if (pframeMain)
967 // Don't repaint too often
968 static int64 nLastRepaintRequest;
969 if (GetTimeMillis() - nLastRepaintRequest < 100)
971 nNeedRepaint++;
972 return;
974 nLastRepaintRequest = GetTimeMillis();
976 printf("MainFrameRepaint\n");
977 wxPaintEvent event;
978 pframeMain->fRefresh = true;
979 pframeMain->GetEventHandler()->AddPendingEvent(event);
983 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
985 // Skip lets the listctrl do the paint, we're just hooking the message
986 event.Skip();
988 if (ptaskbaricon)
989 ptaskbaricon->UpdateTooltip();
992 // Slower stuff
994 static int nTransactionCount;
995 bool fPaintedBalance = false;
996 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
998 nLastRepaint = nNeedRepaint;
999 nLastRepaintTime = GetTimeMillis();
1001 // Update listctrl contents
1002 if (!vWalletUpdated.empty())
1004 TRY_CRITICAL_BLOCK(cs_mapWallet)
1006 string strTop;
1007 if (m_listCtrl->GetItemCount())
1008 strTop = (string)m_listCtrl->GetItemText(0);
1009 foreach(uint256 hash, vWalletUpdated)
1011 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1012 if (mi != mapWallet.end())
1013 InsertTransaction((*mi).second, false);
1015 vWalletUpdated.clear();
1016 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
1017 m_listCtrl->ScrollList(0, INT_MIN/2);
1021 // Balance total
1022 TRY_CRITICAL_BLOCK(cs_mapWallet)
1024 fPaintedBalance = true;
1025 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
1027 // Count hidden and multi-line transactions
1028 nTransactionCount = 0;
1029 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
1031 CWalletTx& wtx = (*it).second;
1032 nTransactionCount += wtx.nLinesDisplayed;
1036 if (!vWalletUpdated.empty() || !fPaintedBalance)
1037 nNeedRepaint++;
1039 // Update status column of visible items only
1040 RefreshStatusColumn();
1042 // Update status bar
1043 static string strPrevWarning;
1044 string strWarning = GetWarnings("statusbar");
1045 if (strWarning != "")
1046 m_statusBar->SetStatusText(string(" ") + _(strWarning), 0);
1047 else if (strPrevWarning != "")
1048 m_statusBar->SetStatusText("", 0);
1049 strPrevWarning = strWarning;
1051 string strGen = "";
1052 if (fGenerateBitcoins)
1053 strGen = _(" Generating");
1054 if (fGenerateBitcoins && vNodes.empty())
1055 strGen = _("(not connected)");
1056 m_statusBar->SetStatusText(strGen, 1);
1058 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight, nTransactionCount);
1059 m_statusBar->SetStatusText(strStatus, 2);
1061 // Update receiving address
1062 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1063 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1064 m_textCtrlAddress->SetValue(strDefaultAddress);
1068 void UIThreadCall(boost::function0<void> fn)
1070 // Call this with a function object created with bind.
1071 // bind needs all parameters to match the function's expected types
1072 // and all default parameters specified. Some examples:
1073 // UIThreadCall(bind(wxBell));
1074 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1075 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1076 if (pframeMain)
1078 wxCommandEvent event(wxEVT_UITHREADCALL);
1079 event.SetClientData((void*)new boost::function0<void>(fn));
1080 pframeMain->GetEventHandler()->AddPendingEvent(event);
1084 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1086 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1087 (*pfn)();
1088 delete pfn;
1091 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1093 // File->Exit
1094 Close(true);
1097 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1099 // Options->Generate Coins
1100 GenerateBitcoins(event.IsChecked());
1103 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1105 event.Check(fGenerateBitcoins);
1108 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1110 // Options->Your Receiving Addresses
1111 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1112 if (!dialog.ShowModal())
1113 return;
1116 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1118 // Options->Options
1119 COptionsDialog dialog(this);
1120 dialog.ShowModal();
1123 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1125 // Help->About
1126 CAboutDialog dialog(this);
1127 dialog.ShowModal();
1130 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1132 // Toolbar: Send
1133 CSendDialog dialog(this);
1134 dialog.ShowModal();
1137 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1139 // Toolbar: Address Book
1140 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1141 if (dialogAddr.ShowModal() == 2)
1143 // Send
1144 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1145 dialogSend.ShowModal();
1149 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1151 // Automatically select-all when entering window
1152 event.Skip();
1153 m_textCtrlAddress->SetSelection(-1, -1);
1154 fOnSetFocusAddress = true;
1157 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1159 event.Skip();
1160 if (fOnSetFocusAddress)
1161 m_textCtrlAddress->SetSelection(-1, -1);
1162 fOnSetFocusAddress = false;
1165 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1167 // Ask name
1168 CGetTextFromUserDialog dialog(this,
1169 _("New Receiving Address"),
1170 _("You should use a new address for each payment you receive.\n\nLabel"),
1171 "");
1172 if (!dialog.ShowModal())
1173 return;
1174 string strName = dialog.GetValue();
1176 // Generate new key
1177 string strAddress = PubKeyToAddress(GetKeyFromKeyPool());
1179 // Save
1180 SetAddressBookName(strAddress, strName);
1181 SetDefaultReceivingAddress(strAddress);
1184 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1186 // Copy address box to clipboard
1187 if (wxTheClipboard->Open())
1189 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1190 wxTheClipboard->Close();
1194 void CMainFrame::OnListItemActivated(wxListEvent& event)
1196 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1197 CWalletTx wtx;
1198 CRITICAL_BLOCK(cs_mapWallet)
1200 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1201 if (mi == mapWallet.end())
1203 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1204 return;
1206 wtx = (*mi).second;
1208 CTxDetailsDialog dialog(this, wtx);
1209 dialog.ShowModal();
1210 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1211 //pdialog->Show();
1219 //////////////////////////////////////////////////////////////////////////////
1221 // CTxDetailsDialog
1224 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1226 CRITICAL_BLOCK(cs_mapAddressBook)
1228 string strHTML;
1229 strHTML.reserve(4000);
1230 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1232 int64 nTime = wtx.GetTxTime();
1233 int64 nCredit = wtx.GetCredit();
1234 int64 nDebit = wtx.GetDebit();
1235 int64 nNet = nCredit - nDebit;
1239 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1240 int nRequests = wtx.GetRequestCount();
1241 if (nRequests != -1)
1243 if (nRequests == 0)
1244 strHTML += _(", has not been successfully broadcast yet");
1245 else if (nRequests == 1)
1246 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1247 else
1248 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1250 strHTML += "<br>";
1252 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1256 // From
1258 if (wtx.IsCoinBase())
1260 strHTML += _("<b>Source:</b> Generated<br>");
1262 else if (!wtx.mapValue["from"].empty())
1264 // Online transaction
1265 if (!wtx.mapValue["from"].empty())
1266 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1268 else
1270 // Offline transaction
1271 if (nNet > 0)
1273 // Credit
1274 foreach(const CTxOut& txout, wtx.vout)
1276 if (txout.IsMine())
1278 vector<unsigned char> vchPubKey;
1279 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1281 string strAddress = PubKeyToAddress(vchPubKey);
1282 if (mapAddressBook.count(strAddress))
1284 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1285 strHTML += _("<b>To:</b> ");
1286 strHTML += HtmlEscape(strAddress);
1287 if (!mapAddressBook[strAddress].empty())
1288 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1289 else
1290 strHTML += _(" (yours)");
1291 strHTML += "<br>";
1294 break;
1302 // To
1304 string strAddress;
1305 if (!wtx.mapValue["to"].empty())
1307 // Online transaction
1308 strAddress = wtx.mapValue["to"];
1309 strHTML += _("<b>To:</b> ");
1310 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1311 strHTML += mapAddressBook[strAddress] + " ";
1312 strHTML += HtmlEscape(strAddress) + "<br>";
1317 // Amount
1319 if (wtx.IsCoinBase() && nCredit == 0)
1322 // Coinbase
1324 int64 nUnmatured = 0;
1325 foreach(const CTxOut& txout, wtx.vout)
1326 nUnmatured += txout.GetCredit();
1327 strHTML += _("<b>Credit:</b> ");
1328 if (wtx.IsInMainChain())
1329 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1330 else
1331 strHTML += _("(not accepted)");
1332 strHTML += "<br>";
1334 else if (nNet > 0)
1337 // Credit
1339 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1341 else
1343 bool fAllFromMe = true;
1344 foreach(const CTxIn& txin, wtx.vin)
1345 fAllFromMe = fAllFromMe && txin.IsMine();
1347 bool fAllToMe = true;
1348 foreach(const CTxOut& txout, wtx.vout)
1349 fAllToMe = fAllToMe && txout.IsMine();
1351 if (fAllFromMe)
1354 // Debit
1356 foreach(const CTxOut& txout, wtx.vout)
1358 if (txout.IsMine())
1359 continue;
1361 if (wtx.mapValue["to"].empty())
1363 // Offline transaction
1364 uint160 hash160;
1365 if (ExtractHash160(txout.scriptPubKey, hash160))
1367 string strAddress = Hash160ToAddress(hash160);
1368 strHTML += _("<b>To:</b> ");
1369 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1370 strHTML += mapAddressBook[strAddress] + " ";
1371 strHTML += strAddress;
1372 strHTML += "<br>";
1376 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1379 if (fAllToMe)
1381 // Payment to self
1382 int64 nChange = wtx.GetChange();
1383 int64 nValue = nCredit - nChange;
1384 strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1385 strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1388 int64 nTxFee = nDebit - wtx.GetValueOut();
1389 if (nTxFee > 0)
1390 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1392 else
1395 // Mixed debit transaction
1397 foreach(const CTxIn& txin, wtx.vin)
1398 if (txin.IsMine())
1399 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1400 foreach(const CTxOut& txout, wtx.vout)
1401 if (txout.IsMine())
1402 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1406 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1410 // Message
1412 if (!wtx.mapValue["message"].empty())
1413 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1414 if (!wtx.mapValue["comment"].empty())
1415 strHTML += string() + "<br><b>" + _("Comment:") + "</b><br>" + HtmlEscape(wtx.mapValue["comment"], true) + "<br>";
1417 if (wtx.IsCoinBase())
1418 strHTML += string() + "<br>" + _("Generated coins must wait 120 blocks before they can be spent. When you generated this block, it was broadcast to the network to be added to the block chain. If it fails to get into the chain, it will change to \"not accepted\" and not be spendable. This may occasionally happen if another node generates a block within a few seconds of yours.") + "<br>";
1422 // Debug view
1424 if (fDebug)
1426 strHTML += "<hr><br>debug print<br><br>";
1427 foreach(const CTxIn& txin, wtx.vin)
1428 if (txin.IsMine())
1429 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1430 foreach(const CTxOut& txout, wtx.vout)
1431 if (txout.IsMine())
1432 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1434 strHTML += "<br><b>Transaction:</b><br>";
1435 strHTML += HtmlEscape(wtx.ToString(), true);
1437 strHTML += "<br><b>Inputs:</b><br>";
1438 CRITICAL_BLOCK(cs_mapWallet)
1440 foreach(const CTxIn& txin, wtx.vin)
1442 COutPoint prevout = txin.prevout;
1443 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1444 if (mi != mapWallet.end())
1446 const CWalletTx& prev = (*mi).second;
1447 if (prevout.n < prev.vout.size())
1449 strHTML += HtmlEscape(prev.ToString(), true);
1450 strHTML += " &nbsp;&nbsp; " + FormatTxStatus(prev) + ", ";
1451 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1460 strHTML += "</font></html>";
1461 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1462 m_htmlWin->SetPage(strHTML);
1463 m_buttonOK->SetFocus();
1467 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1469 EndModal(false);
1477 //////////////////////////////////////////////////////////////////////////////
1479 // Startup folder
1482 #ifdef __WXMSW__
1483 string StartupShortcutPath()
1485 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
1488 bool GetStartOnSystemStartup()
1490 return filesystem::exists(StartupShortcutPath().c_str());
1493 void SetStartOnSystemStartup(bool fAutoStart)
1495 // If the shortcut exists already, remove it for updating
1496 remove(StartupShortcutPath().c_str());
1498 if (fAutoStart)
1500 CoInitialize(NULL);
1502 // Get a pointer to the IShellLink interface.
1503 IShellLink* psl = NULL;
1504 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
1505 CLSCTX_INPROC_SERVER, IID_IShellLink,
1506 reinterpret_cast<void**>(&psl));
1508 if (SUCCEEDED(hres))
1510 // Get the current executable path
1511 TCHAR pszExePath[MAX_PATH];
1512 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
1514 // Set the path to the shortcut target
1515 psl->SetPath(pszExePath);
1516 PathRemoveFileSpec(pszExePath);
1517 psl->SetWorkingDirectory(pszExePath);
1518 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
1520 // Query IShellLink for the IPersistFile interface for
1521 // saving the shortcut in persistent storage.
1522 IPersistFile* ppf = NULL;
1523 hres = psl->QueryInterface(IID_IPersistFile,
1524 reinterpret_cast<void**>(&ppf));
1525 if (SUCCEEDED(hres))
1527 WCHAR pwsz[MAX_PATH];
1528 // Ensure that the string is ANSI.
1529 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
1530 // Save the link by calling IPersistFile::Save.
1531 hres = ppf->Save(pwsz, TRUE);
1532 ppf->Release();
1534 psl->Release();
1536 CoUninitialize();
1540 #elif defined(__WXGTK__)
1542 // Follow the Desktop Application Autostart Spec:
1543 // http://standards.freedesktop.org/autostart-spec/autostart-spec-latest.html
1545 boost::filesystem::path GetAutostartDir()
1547 namespace fs = boost::filesystem;
1549 char* pszConfigHome = getenv("XDG_CONFIG_HOME");
1550 if (pszConfigHome) return fs::path(pszConfigHome) / fs::path("autostart");
1551 char* pszHome = getenv("HOME");
1552 if (pszHome) return fs::path(pszHome) / fs::path(".config/autostart");
1553 return fs::path();
1556 boost::filesystem::path GetAutostartFilePath()
1558 return GetAutostartDir() / boost::filesystem::path("bitcoin.desktop");
1561 bool GetStartOnSystemStartup()
1563 boost::filesystem::ifstream optionFile(GetAutostartFilePath());
1564 if (!optionFile.good())
1565 return false;
1566 // Scan through file for "Hidden=true":
1567 string line;
1568 while (!optionFile.eof())
1570 getline(optionFile, line);
1571 if (line.find("Hidden") != string::npos &&
1572 line.find("true") != string::npos)
1573 return false;
1575 optionFile.close();
1577 return true;
1580 void SetStartOnSystemStartup(bool fAutoStart)
1582 if (!fAutoStart)
1584 unlink(GetAutostartFilePath().native_file_string().c_str());
1586 else
1588 char pszExePath[MAX_PATH+1];
1589 memset(pszExePath, 0, sizeof(pszExePath));
1590 if (readlink("/proc/self/exe", pszExePath, sizeof(pszExePath)-1) == -1)
1591 return;
1593 boost::filesystem::create_directories(GetAutostartDir());
1595 boost::filesystem::ofstream optionFile(GetAutostartFilePath(), ios_base::out|ios_base::trunc);
1596 if (!optionFile.good())
1598 wxMessageBox(_("Cannot write autostart/bitcoin.desktop file"), "Bitcoin");
1599 return;
1601 // Write a bitcoin.desktop file to the autostart directory:
1602 optionFile << "[Desktop Entry]\n";
1603 optionFile << "Type=Application\n";
1604 optionFile << "Name=Bitcoin\n";
1605 optionFile << "Exec=" << pszExePath << "\n";
1606 optionFile << "Terminal=false\n";
1607 optionFile << "Hidden=false\n";
1608 optionFile.close();
1611 #else
1613 // TODO: OSX startup stuff; see:
1614 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPSystemStartup/Articles/CustomLogin.html
1616 bool GetStartOnSystemStartup() { return false; }
1617 void SetStartOnSystemStartup(bool fAutoStart) { }
1619 #endif
1626 //////////////////////////////////////////////////////////////////////////////
1628 // COptionsDialog
1631 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1633 // Set up list box of page choices
1634 m_listBox->Append(_("Main"));
1635 //m_listBox->Append(_("Test 2"));
1636 m_listBox->SetSelection(0);
1637 SelectPage(0);
1638 #ifndef __WXMSW__
1639 SetSize(1.0 * GetSize().GetWidth(), 1.2 * GetSize().GetHeight());
1640 #endif
1641 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
1642 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1643 if (!GetBoolArg("-minimizetotray"))
1645 // Minimize to tray is just too buggy on Linux
1646 fMinimizeToTray = false;
1647 m_checkBoxMinimizeToTray->SetValue(false);
1648 m_checkBoxMinimizeToTray->Enable(false);
1649 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1651 #endif
1652 #ifdef __WXMAC_OSX__
1653 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1654 #endif
1656 // Init values
1657 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1658 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1659 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1660 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1661 int nProcessors = wxThread::GetCPUCount();
1662 if (nProcessors < 1)
1663 nProcessors = 999;
1664 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1665 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1666 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1667 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1668 m_checkBoxUseProxy->SetValue(fUseProxy);
1669 m_textCtrlProxyIP->Enable(fUseProxy);
1670 m_textCtrlProxyPort->Enable(fUseProxy);
1671 m_staticTextProxyIP->Enable(fUseProxy);
1672 m_staticTextProxyPort->Enable(fUseProxy);
1673 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1674 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1676 m_buttonOK->SetFocus();
1679 void COptionsDialog::SelectPage(int nPage)
1681 m_panelMain->Show(nPage == 0);
1682 m_panelTest2->Show(nPage == 1);
1684 m_scrolledWindow->Layout();
1685 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1688 void COptionsDialog::OnListBox(wxCommandEvent& event)
1690 SelectPage(event.GetSelection());
1693 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1695 event.Skip();
1696 int64 nTmp = nTransactionFee;
1697 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1698 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1701 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1703 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1706 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1708 m_textCtrlProxyIP->Enable(event.IsChecked());
1709 m_textCtrlProxyPort->Enable(event.IsChecked());
1710 m_staticTextProxyIP->Enable(event.IsChecked());
1711 m_staticTextProxyPort->Enable(event.IsChecked());
1714 CAddress COptionsDialog::GetProxyAddr()
1716 // Be careful about byte order, addr.ip and addr.port are big endian
1717 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1718 if (addr.ip == INADDR_NONE)
1719 addr.ip = addrProxy.ip;
1720 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1721 addr.port = htons(nPort);
1722 if (nPort <= 0 || nPort > USHRT_MAX)
1723 addr.port = addrProxy.port;
1724 return addr;
1727 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1729 event.Skip();
1730 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1731 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1735 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1737 OnButtonApply(event);
1738 EndModal(false);
1741 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1743 EndModal(false);
1746 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1748 CWalletDB walletdb;
1750 int64 nPrevTransactionFee = nTransactionFee;
1751 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1752 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1754 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1755 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1757 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1758 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1760 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1762 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1763 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1765 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1766 GenerateBitcoins(fGenerateBitcoins);
1768 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1770 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1771 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1774 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1776 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1777 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1778 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1781 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1783 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1784 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1787 fUseProxy = m_checkBoxUseProxy->GetValue();
1788 walletdb.WriteSetting("fUseProxy", fUseProxy);
1790 addrProxy = GetProxyAddr();
1791 walletdb.WriteSetting("addrProxy", addrProxy);
1799 //////////////////////////////////////////////////////////////////////////////
1801 // CAboutDialog
1804 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1806 m_staticTextVersion->SetLabel(strprintf(_("version %s%s BETA"), FormatVersion(VERSION).c_str(), pszSubVer));
1808 // Change (c) into UTF-8 or ANSI copyright symbol
1809 wxString str = m_staticTextMain->GetLabel();
1810 #if wxUSE_UNICODE
1811 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1812 #else
1813 str.Replace("(c)", "\xA9");
1814 #endif
1815 m_staticTextMain->SetLabel(str);
1816 #ifndef __WXMSW__
1817 // Resize on Linux to make the window fit the text.
1818 // The text was wrapped manually rather than using the Wrap setting because
1819 // the wrap would be too small on Linux and it can't be changed at this point.
1820 wxFont fontTmp = m_staticTextMain->GetFont();
1821 if (fontTmp.GetPointSize() > 8);
1822 fontTmp.SetPointSize(8);
1823 m_staticTextMain->SetFont(fontTmp);
1824 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1825 #endif
1828 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1830 EndModal(false);
1838 //////////////////////////////////////////////////////////////////////////////
1840 // CSendDialog
1843 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1845 // Init
1846 m_textCtrlAddress->SetValue(strAddress);
1847 m_choiceTransferType->SetSelection(0);
1848 m_bitmapCheckMark->Show(false);
1849 fEnabledPrev = true;
1850 m_textCtrlAddress->SetFocus();
1851 //// todo: should add a display of your balance for convenience
1852 #ifndef __WXMSW__
1853 wxFont fontTmp = m_staticTextInstructions->GetFont();
1854 if (fontTmp.GetPointSize() > 9);
1855 fontTmp.SetPointSize(9);
1856 m_staticTextInstructions->SetFont(fontTmp);
1857 SetSize(725, 380);
1858 #endif
1860 // Set Icon
1861 wxIcon iconSend;
1862 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1863 SetIcon(iconSend);
1865 wxCommandEvent event;
1866 OnTextAddress(event);
1868 // Fixup the tab order
1869 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1870 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1871 this->Layout();
1874 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1876 // Check mark
1877 event.Skip();
1878 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1879 m_bitmapCheckMark->Show(fBitcoinAddress);
1881 // Grey out message if bitcoin address
1882 bool fEnable = !fBitcoinAddress;
1883 m_staticTextFrom->Enable(fEnable);
1884 m_textCtrlFrom->Enable(fEnable);
1885 m_staticTextMessage->Enable(fEnable);
1886 m_textCtrlMessage->Enable(fEnable);
1887 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1888 if (!fEnable && fEnabledPrev)
1890 strFromSave = m_textCtrlFrom->GetValue();
1891 strMessageSave = m_textCtrlMessage->GetValue();
1892 m_textCtrlFrom->SetValue(_("n/a"));
1893 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1895 else if (fEnable && !fEnabledPrev)
1897 m_textCtrlFrom->SetValue(strFromSave);
1898 m_textCtrlMessage->SetValue(strMessageSave);
1900 fEnabledPrev = fEnable;
1903 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1905 // Reformat the amount
1906 event.Skip();
1907 if (m_textCtrlAmount->GetValue().Trim().empty())
1908 return;
1909 int64 nTmp;
1910 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1911 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1914 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1916 // Open address book
1917 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1918 if (dialog.ShowModal())
1919 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1922 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1924 // Copy clipboard to address box
1925 if (wxTheClipboard->Open())
1927 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1929 wxTextDataObject data;
1930 wxTheClipboard->GetData(data);
1931 m_textCtrlAddress->SetValue(data.GetText());
1933 wxTheClipboard->Close();
1937 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1939 static CCriticalSection cs_sendlock;
1940 TRY_CRITICAL_BLOCK(cs_sendlock)
1942 CWalletTx wtx;
1943 string strAddress = (string)m_textCtrlAddress->GetValue();
1945 // Parse amount
1946 int64 nValue = 0;
1947 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1949 wxMessageBox(_("Error in amount "), _("Send Coins"));
1950 return;
1952 if (nValue > GetBalance())
1954 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1955 return;
1957 if (nValue + nTransactionFee > GetBalance())
1959 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1960 return;
1963 // Parse bitcoin address
1964 uint160 hash160;
1965 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1967 if (fBitcoinAddress)
1969 // Send to bitcoin address
1970 CScript scriptPubKey;
1971 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1973 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1974 if (strError == "")
1975 wxMessageBox(_("Payment sent "), _("Sending..."));
1976 else if (strError == "ABORTED")
1977 return; // leave send dialog open
1978 else
1980 wxMessageBox(strError + " ", _("Sending..."));
1981 EndModal(false);
1984 else
1986 // Parse IP address
1987 CAddress addr(strAddress);
1988 if (!addr.IsValid())
1990 wxMessageBox(_("Invalid address "), _("Send Coins"));
1991 return;
1994 // Message
1995 wtx.mapValue["to"] = strAddress;
1996 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1997 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1999 // Send to IP address
2000 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
2001 if (!pdialog->ShowModal())
2002 return;
2005 CRITICAL_BLOCK(cs_mapAddressBook)
2006 if (!mapAddressBook.count(strAddress))
2007 SetAddressBookName(strAddress, "");
2009 EndModal(true);
2013 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
2015 // Cancel
2016 EndModal(false);
2024 //////////////////////////////////////////////////////////////////////////////
2026 // CSendingDialog
2029 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
2031 addr = addrIn;
2032 nPrice = nPriceIn;
2033 wtx = wtxIn;
2034 start = wxDateTime::UNow();
2035 memset(pszStatus, 0, sizeof(pszStatus));
2036 fCanCancel = true;
2037 fAbort = false;
2038 fSuccess = false;
2039 fUIDone = false;
2040 fWorkDone = false;
2041 #ifndef __WXMSW__
2042 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
2043 #endif
2045 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
2046 m_textCtrlStatus->SetValue("");
2048 CreateThread(SendingDialogStartTransfer, this);
2051 CSendingDialog::~CSendingDialog()
2053 printf("~CSendingDialog()\n");
2056 void CSendingDialog::Close()
2058 // Last one out turn out the lights.
2059 // fWorkDone signals that work side is done and UI thread should call destroy.
2060 // fUIDone signals that UI window has closed and work thread should call destroy.
2061 // This allows the window to disappear and end modality when cancelled
2062 // without making the user wait for ConnectNode to return. The dialog object
2063 // hangs around in the background until the work thread exits.
2064 if (IsModal())
2065 EndModal(fSuccess);
2066 else
2067 Show(false);
2068 if (fWorkDone)
2069 Destroy();
2070 else
2071 fUIDone = true;
2074 void CSendingDialog::OnClose(wxCloseEvent& event)
2076 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
2078 Close();
2080 else
2082 event.Veto();
2083 wxCommandEvent cmdevent;
2084 OnButtonCancel(cmdevent);
2088 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
2090 if (fWorkDone)
2091 Close();
2094 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
2096 if (fCanCancel)
2097 fAbort = true;
2100 void CSendingDialog::OnPaint(wxPaintEvent& event)
2102 event.Skip();
2103 if (strlen(pszStatus) > 130)
2104 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
2105 else
2106 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
2107 m_staticTextSending->SetFocus();
2108 if (!fCanCancel)
2109 m_buttonCancel->Enable(false);
2110 if (fWorkDone)
2112 m_buttonOK->Enable(true);
2113 m_buttonOK->SetFocus();
2114 m_buttonCancel->Enable(false);
2116 if (fAbort && fCanCancel && IsShown())
2118 strcpy(pszStatus, _("CANCELLED"));
2119 m_buttonOK->Enable(true);
2120 m_buttonOK->SetFocus();
2121 m_buttonCancel->Enable(false);
2122 m_buttonCancel->SetLabel(_("Cancelled"));
2123 Close();
2124 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
2130 // Everything from here on is not in the UI thread and must only communicate
2131 // with the rest of the dialog through variables and calling repaint.
2134 void CSendingDialog::Repaint()
2136 Refresh();
2137 wxPaintEvent event;
2138 GetEventHandler()->AddPendingEvent(event);
2141 bool CSendingDialog::Status()
2143 if (fUIDone)
2145 Destroy();
2146 return false;
2148 if (fAbort && fCanCancel)
2150 memset(pszStatus, 0, 10);
2151 strcpy(pszStatus, _("CANCELLED"));
2152 Repaint();
2153 fWorkDone = true;
2154 return false;
2156 return true;
2159 bool CSendingDialog::Status(const string& str)
2161 if (!Status())
2162 return false;
2164 // This can be read by the UI thread at any time,
2165 // so copy in a way that can be read cleanly at all times.
2166 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
2167 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
2169 Repaint();
2170 return true;
2173 bool CSendingDialog::Error(const string& str)
2175 fCanCancel = false;
2176 fWorkDone = true;
2177 Status(string(_("Error: ")) + str);
2178 return false;
2181 void SendingDialogStartTransfer(void* parg)
2183 ((CSendingDialog*)parg)->StartTransfer();
2186 void CSendingDialog::StartTransfer()
2188 // Make sure we have enough money
2189 if (nPrice + nTransactionFee > GetBalance())
2191 Error(_("Insufficient funds"));
2192 return;
2195 // We may have connected already for product details
2196 if (!Status(_("Connecting...")))
2197 return;
2198 CNode* pnode = ConnectNode(addr, 15 * 60);
2199 if (!pnode)
2201 Error(_("Unable to connect"));
2202 return;
2205 // Send order to seller, with response going to OnReply2 via event handler
2206 if (!Status(_("Requesting public key...")))
2207 return;
2208 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2211 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2213 ((CSendingDialog*)parg)->OnReply2(vRecv);
2216 void CSendingDialog::OnReply2(CDataStream& vRecv)
2218 if (!Status(_("Received public key...")))
2219 return;
2221 CScript scriptPubKey;
2222 int nRet;
2225 vRecv >> nRet;
2226 if (nRet > 0)
2228 string strMessage;
2229 if (!vRecv.empty())
2230 vRecv >> strMessage;
2231 if (nRet == 2)
2232 Error(_("Recipient is not accepting transactions sent by IP address"));
2233 else
2234 Error(_("Transfer was not accepted"));
2235 //// todo: enlarge the window and enable a hidden white box to put seller's message
2236 return;
2238 vRecv >> scriptPubKey;
2240 catch (...)
2242 //// what do we want to do about this?
2243 Error(_("Invalid response received"));
2244 return;
2247 // Pause to give the user a chance to cancel
2248 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2250 Sleep(200);
2251 if (!Status())
2252 return;
2255 CRITICAL_BLOCK(cs_main)
2257 // Pay
2258 if (!Status(_("Creating transaction...")))
2259 return;
2260 if (nPrice + nTransactionFee > GetBalance())
2262 Error(_("Insufficient funds"));
2263 return;
2265 CReserveKey reservekey;
2266 int64 nFeeRequired;
2267 if (!CreateTransaction(scriptPubKey, nPrice, wtx, reservekey, nFeeRequired))
2269 if (nPrice + nFeeRequired > GetBalance())
2270 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2271 else
2272 Error(_("Transaction creation failed"));
2273 return;
2276 // Transaction fee
2277 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2279 Error(_("Transaction aborted"));
2280 return;
2283 // Make sure we're still connected
2284 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2285 if (!pnode)
2287 Error(_("Lost connection, transaction cancelled"));
2288 return;
2291 // Last chance to cancel
2292 Sleep(50);
2293 if (!Status())
2294 return;
2295 fCanCancel = false;
2296 if (fAbort)
2298 fCanCancel = true;
2299 if (!Status())
2300 return;
2301 fCanCancel = false;
2303 if (!Status(_("Sending payment...")))
2304 return;
2306 // Commit
2307 if (!CommitTransaction(wtx, reservekey))
2309 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."));
2310 return;
2313 // Send payment tx to seller, with response going to OnReply3 via event handler
2314 CWalletTx wtxSend = wtx;
2315 wtxSend.fFromMe = false;
2316 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2318 Status(_("Waiting for confirmation..."));
2319 MainFrameRepaint();
2323 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2325 ((CSendingDialog*)parg)->OnReply3(vRecv);
2328 void CSendingDialog::OnReply3(CDataStream& vRecv)
2330 int nRet;
2333 vRecv >> nRet;
2334 if (nRet > 0)
2336 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2337 "The transaction is recorded and will credit to the recipient,\n"
2338 "but the comment information will be blank."));
2339 return;
2342 catch (...)
2344 //// what do we want to do about this?
2345 Error(_("Payment was sent, but an invalid response was received"));
2346 return;
2349 fSuccess = true;
2350 fWorkDone = true;
2351 Status(_("Payment completed"));
2359 //////////////////////////////////////////////////////////////////////////////
2361 // CAddressBookDialog
2364 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2366 // Set initially selected page
2367 wxNotebookEvent event;
2368 event.SetSelection(nPageIn);
2369 OnNotebookPageChanged(event);
2370 m_notebook->ChangeSelection(nPageIn);
2372 fDuringSend = fDuringSendIn;
2373 if (!fDuringSend)
2374 m_buttonCancel->Show(false);
2376 // Set Icon
2377 wxIcon iconAddressBook;
2378 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2379 SetIcon(iconAddressBook);
2381 // Init column headers
2382 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2383 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2384 m_listCtrlSending->SetFocus();
2385 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2386 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2387 m_listCtrlReceiving->SetFocus();
2389 // Fill listctrl with address book data
2390 CRITICAL_BLOCK(cs_mapKeys)
2391 CRITICAL_BLOCK(cs_mapAddressBook)
2393 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2394 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2396 string strAddress = item.first;
2397 string strName = item.second;
2398 uint160 hash160;
2399 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2400 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2401 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2402 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2403 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2408 wxString CAddressBookDialog::GetSelectedAddress()
2410 int nIndex = GetSelection(m_listCtrl);
2411 if (nIndex == -1)
2412 return "";
2413 return GetItemText(m_listCtrl, nIndex, 1);
2416 wxString CAddressBookDialog::GetSelectedSendingAddress()
2418 int nIndex = GetSelection(m_listCtrlSending);
2419 if (nIndex == -1)
2420 return "";
2421 return GetItemText(m_listCtrlSending, nIndex, 1);
2424 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2426 int nIndex = GetSelection(m_listCtrlReceiving);
2427 if (nIndex == -1)
2428 return "";
2429 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2432 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2434 event.Skip();
2435 nPage = event.GetSelection();
2436 if (nPage == SENDING)
2437 m_listCtrl = m_listCtrlSending;
2438 else if (nPage == RECEIVING)
2439 m_listCtrl = m_listCtrlReceiving;
2440 m_buttonDelete->Show(nPage == SENDING);
2441 m_buttonCopy->Show(nPage == RECEIVING);
2442 this->Layout();
2443 m_listCtrl->SetFocus();
2446 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2448 // Update address book with edited name
2449 event.Skip();
2450 if (event.IsEditCancelled())
2451 return;
2452 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2453 SetAddressBookName(strAddress, string(event.GetText()));
2454 pframeMain->RefreshListCtrl();
2457 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2459 event.Skip();
2460 if (nPage == RECEIVING)
2461 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2464 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2466 event.Skip();
2467 if (fDuringSend)
2469 // Doubleclick returns selection
2470 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2471 return;
2474 // Doubleclick edits item
2475 wxCommandEvent event2;
2476 OnButtonEdit(event2);
2479 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2481 if (nPage != SENDING)
2482 return;
2483 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2485 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2487 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2488 CWalletDB().EraseName(strAddress);
2489 m_listCtrl->DeleteItem(nIndex);
2492 pframeMain->RefreshListCtrl();
2495 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2497 // Copy address box to clipboard
2498 if (wxTheClipboard->Open())
2500 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2501 wxTheClipboard->Close();
2505 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2507 uint160 hash160;
2508 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2509 if (fMine)
2510 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2511 return fMine;
2514 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2516 int nIndex = GetSelection(m_listCtrl);
2517 if (nIndex == -1)
2518 return;
2519 string strName = (string)m_listCtrl->GetItemText(nIndex);
2520 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2521 string strAddressOrg = strAddress;
2523 if (nPage == SENDING)
2525 // Ask name and address
2528 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2529 if (!dialog.ShowModal())
2530 return;
2531 strName = dialog.GetValue1();
2532 strAddress = dialog.GetValue2();
2534 while (CheckIfMine(strAddress, _("Edit Address")));
2537 else if (nPage == RECEIVING)
2539 // Ask name
2540 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2541 if (!dialog.ShowModal())
2542 return;
2543 strName = dialog.GetValue();
2546 // Write back
2547 if (strAddress != strAddressOrg)
2548 CWalletDB().EraseName(strAddressOrg);
2549 SetAddressBookName(strAddress, strName);
2550 m_listCtrl->SetItem(nIndex, 1, strAddress);
2551 m_listCtrl->SetItemText(nIndex, strName);
2552 pframeMain->RefreshListCtrl();
2555 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2557 string strName;
2558 string strAddress;
2560 if (nPage == SENDING)
2562 // Ask name and address
2565 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2566 if (!dialog.ShowModal())
2567 return;
2568 strName = dialog.GetValue1();
2569 strAddress = dialog.GetValue2();
2571 while (CheckIfMine(strAddress, _("Add Address")));
2573 else if (nPage == RECEIVING)
2575 // Ask name
2576 CGetTextFromUserDialog dialog(this,
2577 _("New Receiving Address"),
2578 _("You should use a new address for each payment you receive.\n\nLabel"),
2579 "");
2580 if (!dialog.ShowModal())
2581 return;
2582 strName = dialog.GetValue();
2584 // Generate new key
2585 strAddress = PubKeyToAddress(GetKeyFromKeyPool());
2588 // Add to list and select it
2589 SetAddressBookName(strAddress, strName);
2590 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2591 SetSelection(m_listCtrl, nIndex);
2592 m_listCtrl->SetFocus();
2593 if (nPage == SENDING)
2594 pframeMain->RefreshListCtrl();
2597 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2599 // OK
2600 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2603 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2605 // Cancel
2606 EndModal(0);
2609 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2611 // Close
2612 EndModal(0);
2620 //////////////////////////////////////////////////////////////////////////////
2622 // CMyTaskBarIcon
2625 enum
2627 ID_TASKBAR_RESTORE = 10001,
2628 ID_TASKBAR_OPTIONS,
2629 ID_TASKBAR_GENERATE,
2630 ID_TASKBAR_EXIT,
2633 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2634 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2635 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2636 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2637 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2638 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2639 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2640 END_EVENT_TABLE()
2642 void CMyTaskBarIcon::Show(bool fShow)
2644 static char pszPrevTip[200];
2645 if (fShow)
2647 string strTooltip = _("Bitcoin");
2648 if (fGenerateBitcoins)
2649 strTooltip = _("Bitcoin - Generating");
2650 if (fGenerateBitcoins && vNodes.empty())
2651 strTooltip = _("Bitcoin - (not connected)");
2653 // Optimization, only update when changed, using char array to be reentrant
2654 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2656 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2657 #ifdef __WXMSW__
2658 // somehow it'll choose the wrong size and scale it down if
2659 // we use the main icon, so we hand it one with only 16x16
2660 SetIcon(wxICON(favicon), strTooltip);
2661 #else
2662 SetIcon(bitcoin80_xpm, strTooltip);
2663 #endif
2666 else
2668 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2669 RemoveIcon();
2673 void CMyTaskBarIcon::Hide()
2675 Show(false);
2678 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2680 Restore();
2683 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2685 Restore();
2688 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2690 // Since it's modal, get the main window to do it
2691 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2692 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2695 void CMyTaskBarIcon::Restore()
2697 pframeMain->Show();
2698 wxIconizeEvent event(0, false);
2699 pframeMain->GetEventHandler()->AddPendingEvent(event);
2700 pframeMain->Iconize(false);
2701 pframeMain->Raise();
2704 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2706 GenerateBitcoins(event.IsChecked());
2709 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2711 event.Check(fGenerateBitcoins);
2714 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2716 pframeMain->Close(true);
2719 void CMyTaskBarIcon::UpdateTooltip()
2721 if (IsIconInstalled())
2722 Show(true);
2725 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2727 wxMenu* pmenu = new wxMenu;
2728 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2729 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2730 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2731 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2732 pmenu->AppendSeparator();
2733 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2734 #endif
2735 return pmenu;
2743 //////////////////////////////////////////////////////////////////////////////
2745 // CMyApp
2748 void CreateMainWindow()
2750 pframeMain = new CMainFrame(NULL);
2751 if (GetBoolArg("-min"))
2752 pframeMain->Iconize(true);
2753 #if defined(__WXGTK__) || defined(__WXMAC_OSX__)
2754 if (!GetBoolArg("-minimizetotray"))
2755 fMinimizeToTray = false;
2756 #endif
2757 pframeMain->Show(true); // have to show first to get taskbar button to hide
2758 if (fMinimizeToTray && pframeMain->IsIconized())
2759 fClosedToTray = true;
2760 pframeMain->Show(!fClosedToTray);
2761 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2762 CreateThread(ThreadDelayedRepaint, NULL);
2766 // Define a new application
2767 class CMyApp : public wxApp
2769 public:
2770 CMyApp(){};
2771 ~CMyApp(){};
2772 bool OnInit();
2773 bool OnInit2();
2774 int OnExit();
2776 // Hook Initialize so we can start without GUI
2777 virtual bool Initialize(int& argc, wxChar** argv);
2779 // 2nd-level exception handling: we get all the exceptions occurring in any
2780 // event handler here
2781 virtual bool OnExceptionInMainLoop();
2783 // 3rd, and final, level exception handling: whenever an unhandled
2784 // exception is caught, this function is called
2785 virtual void OnUnhandledException();
2787 // and now for something different: this function is called in case of a
2788 // crash (e.g. dereferencing null pointer, division by 0, ...)
2789 virtual void OnFatalException();
2792 IMPLEMENT_APP(CMyApp)
2794 bool CMyApp::Initialize(int& argc, wxChar** argv)
2796 for (int i = 1; i < argc; i++)
2797 if (!IsSwitchChar(argv[i][0]))
2798 fCommandLine = true;
2800 if (!fCommandLine)
2802 // wxApp::Initialize will remove environment-specific parameters,
2803 // so it's too early to call ParseParameters yet
2804 for (int i = 1; i < argc; i++)
2806 wxString str = argv[i];
2807 #ifdef __WXMSW__
2808 if (str.size() >= 1 && str[0] == '/')
2809 str[0] = '-';
2810 char pszLower[MAX_PATH];
2811 strlcpy(pszLower, str.c_str(), sizeof(pszLower));
2812 strlwr(pszLower);
2813 str = pszLower;
2814 #endif
2815 if (str == "-daemon")
2816 fDaemon = true;
2820 #ifdef __WXGTK__
2821 if (fDaemon || fCommandLine)
2823 // Call the original Initialize while suppressing error messages
2824 // and ignoring failure. If unable to initialize GTK, it fails
2825 // near the end so hopefully the last few things don't matter.
2827 wxLogNull logNo;
2828 wxApp::Initialize(argc, argv);
2831 if (fDaemon)
2833 // Daemonize
2834 pid_t pid = fork();
2835 if (pid < 0)
2837 fprintf(stderr, "Error: fork() returned %d errno %d\n", pid, errno);
2838 return false;
2840 if (pid > 0)
2841 pthread_exit((void*)0);
2844 return true;
2846 #endif
2848 return wxApp::Initialize(argc, argv);
2851 bool CMyApp::OnInit()
2853 #if defined(__WXMSW__) && defined(__WXDEBUG__) && defined(GUI)
2854 // Disable malfunctioning wxWidgets debug assertion
2855 extern int g_isPainting;
2856 g_isPainting = 10000;
2857 #endif
2858 #ifdef GUI
2859 wxImage::AddHandler(new wxPNGHandler);
2860 #endif
2861 #if defined(__WXMSW__ ) || defined(__WXMAC_OSX__)
2862 SetAppName("Bitcoin");
2863 #else
2864 SetAppName("bitcoin");
2865 #endif
2866 #ifdef __WXMSW__
2867 #if wxUSE_UNICODE
2868 // Hack to set wxConvLibc codepage to UTF-8 on Windows,
2869 // may break if wxMBConv_win32 implementation in strconv.cpp changes.
2870 class wxMBConv_win32 : public wxMBConv
2872 public:
2873 long m_CodePage;
2874 size_t m_minMBCharWidth;
2876 if (((wxMBConv_win32*)&wxConvLibc)->m_CodePage == CP_ACP)
2877 ((wxMBConv_win32*)&wxConvLibc)->m_CodePage = CP_UTF8;
2878 #endif
2879 #endif
2881 // Load locale/<lang>/LC_MESSAGES/bitcoin.mo language file
2882 g_locale.Init(wxLANGUAGE_DEFAULT, 0);
2883 g_locale.AddCatalogLookupPathPrefix("locale");
2884 #ifndef __WXMSW__
2885 g_locale.AddCatalogLookupPathPrefix("/usr/share/locale");
2886 g_locale.AddCatalogLookupPathPrefix("/usr/local/share/locale");
2887 #endif
2888 g_locale.AddCatalog("wxstd"); // wxWidgets standard translations, if any
2889 g_locale.AddCatalog("bitcoin");
2891 return AppInit(argc, argv);
2894 int CMyApp::OnExit()
2896 Shutdown(NULL);
2897 return wxApp::OnExit();
2900 bool CMyApp::OnExceptionInMainLoop()
2904 throw;
2906 catch (std::exception& e)
2908 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
2909 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2910 Sleep(1000);
2911 throw;
2913 catch (...)
2915 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
2916 wxLogWarning("Unknown exception");
2917 Sleep(1000);
2918 throw;
2920 return true;
2923 void CMyApp::OnUnhandledException()
2925 // this shows how we may let some exception propagate uncaught
2928 throw;
2930 catch (std::exception& e)
2932 PrintException(&e, "CMyApp::OnUnhandledException()");
2933 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
2934 Sleep(1000);
2935 throw;
2937 catch (...)
2939 PrintException(NULL, "CMyApp::OnUnhandledException()");
2940 wxLogWarning("Unknown exception");
2941 Sleep(1000);
2942 throw;
2946 void CMyApp::OnFatalException()
2948 wxMessageBox(_("Program has crashed and will terminate. "), "Bitcoin", wxOK | wxICON_ERROR);