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
26 //////////////////////////////////////////////////////////////////////////////
\r
31 void HandleCtrlA(wxKeyEvent& event)
\r
33 // Ctrl-a select all
\r
35 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
36 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
37 textCtrl->SetSelection(-1, -1);
\r
42 //char pszHourFormat[256];
\r
43 //pszHourFormat[0] = '\0';
\r
44 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
45 //return (pszHourFormat[0] != '0');
\r
49 string DateStr(int64 nTime)
\r
51 // Can only be used safely here in the UI
\r
52 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
55 string DateTimeStr(int64 nTime)
\r
57 // Can only be used safely here in the UI
\r
58 wxDateTime datetime((time_t)nTime);
\r
60 return (string)datetime.Format("%x %H:%M");
\r
62 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
65 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
67 // Helper to simplify access to listctrl
\r
69 item.m_itemId = nIndex;
\r
70 item.m_col = nColumn;
\r
71 item.m_mask = wxLIST_MASK_TEXT;
\r
72 if (!listCtrl->GetItem(item))
\r
74 return item.GetText();
\r
77 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
79 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
80 listCtrl->SetItem(nIndex, 1, str1);
\r
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
86 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
87 listCtrl->SetItem(nIndex, 1, str1);
\r
88 listCtrl->SetItem(nIndex, 2, str2);
\r
89 listCtrl->SetItem(nIndex, 3, str3);
\r
90 listCtrl->SetItem(nIndex, 4, str4);
\r
94 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
96 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
97 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
98 listCtrl->SetItem(nIndex, 1, str1);
\r
99 listCtrl->SetItem(nIndex, 2, str2);
\r
100 listCtrl->SetItem(nIndex, 3, str3);
\r
101 listCtrl->SetItem(nIndex, 4, str4);
\r
105 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
107 int nSize = listCtrl->GetItemCount();
\r
108 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
109 for (int i = 0; i < nSize; i++)
\r
110 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
113 int GetSelection(wxListCtrl* listCtrl)
\r
115 int nSize = listCtrl->GetItemCount();
\r
116 for (int i = 0; i < nSize; i++)
\r
117 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
122 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
125 for (const char* p = psz; *p; p++)
\r
127 if (*p == '<') len += 4;
\r
128 else if (*p == '>') len += 4;
\r
129 else if (*p == '&') len += 5;
\r
130 else if (*p == '"') len += 6;
\r
131 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
\r
132 else if (*p == '\n' && fMultiLine) len += 5;
\r
138 for (const char* p = psz; *p; p++)
\r
140 if (*p == '<') str += "<";
\r
141 else if (*p == '>') str += ">";
\r
142 else if (*p == '&') str += "&";
\r
143 else if (*p == '"') str += """;
\r
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
145 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
152 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
154 return HtmlEscape(str.c_str(), fMultiLine);
\r
157 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
159 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
163 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
166 return wxMessageBox(message, caption, style, parent, x, y);
\r
168 if (wxThread::IsMain() || fDaemon)
\r
170 return wxMessageBox(message, caption, style, parent, x, y);
\r
175 bool fDone = false;
\r
176 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
184 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
\r
186 if (nFeeRequired == 0 || fDaemon)
\r
188 string strMessage = strprintf(
\r
189 _("This transaction is over the size limit. You can still send it for a fee of %s, "
\r
190 "which goes to the nodes that process your transaction and helps to support the network. "
\r
191 "Do you want to pay the fee?"),
\r
192 FormatMoney(nFeeRequired).c_str());
\r
193 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
\r
196 void SetDefaultReceivingAddress(const string& strAddress)
\r
198 // Update main window address and database
\r
199 if (pframeMain == NULL)
\r
201 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
\r
204 if (!AddressToHash160(strAddress, hash160))
\r
206 if (!mapPubKeys.count(hash160))
\r
208 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
209 pframeMain->m_textCtrlAddress->SetValue(strAddress);
\r
222 //////////////////////////////////////////////////////////////////////////////
\r
227 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
229 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
231 // Set initially selected page
\r
232 wxNotebookEvent event;
\r
233 event.SetSelection(0);
\r
234 OnNotebookPageChanged(event);
\r
235 m_notebook->ChangeSelection(0);
\r
238 fRefreshListCtrl = false;
\r
239 fRefreshListCtrlRunning = false;
\r
240 fOnSetFocusAddress = false;
\r
242 m_choiceFilter->SetSelection(0);
\r
243 double dResize = 1.0;
\r
245 SetIcon(wxICON(bitcoin));
\r
247 SetIcon(bitcoin16_xpm);
\r
248 wxFont fontTmp = m_staticText41->GetFont();
\r
249 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
250 m_staticTextBalance->SetFont(fontTmp);
\r
251 m_staticTextBalance->SetSize(140, 17);
\r
252 // resize to fit ubuntu's huge default font
\r
254 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
\r
256 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
257 m_listCtrl->SetFocus();
\r
258 ptaskbaricon = new CMyTaskBarIcon();
\r
260 // Init column headers
\r
261 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
262 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
264 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
\r
265 foreach(wxListCtrl* p, pplistCtrl)
\r
267 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
268 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
269 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
\r
270 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
271 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
272 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
273 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
277 int pnWidths[3] = { -100, 88, 290 };
\r
279 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
280 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
282 m_statusBar->SetFieldsCount(3, pnWidths);
\r
284 // Fill your address text box
\r
285 vector<unsigned char> vchPubKey;
\r
286 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
287 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
289 // Fill listctrl with wallet transactions
\r
293 CMainFrame::~CMainFrame()
\r
296 delete ptaskbaricon;
\r
297 ptaskbaricon = NULL;
\r
300 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
\r
303 nPage = event.GetSelection();
\r
306 m_listCtrl = m_listCtrlAll;
\r
307 fShowGenerated = true;
\r
309 fShowReceived = true;
\r
311 else if (nPage == SENTRECEIVED)
\r
313 m_listCtrl = m_listCtrlSentReceived;
\r
314 fShowGenerated = false;
\r
316 fShowReceived = true;
\r
318 else if (nPage == SENT)
\r
320 m_listCtrl = m_listCtrlSent;
\r
321 fShowGenerated = false;
\r
323 fShowReceived = false;
\r
325 else if (nPage == RECEIVED)
\r
327 m_listCtrl = m_listCtrlReceived;
\r
328 fShowGenerated = false;
\r
330 fShowReceived = true;
\r
333 m_listCtrl->SetFocus();
\r
336 void CMainFrame::OnClose(wxCloseEvent& event)
\r
338 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
340 // Divert close to minimize
\r
342 fClosedToTray = true;
\r
348 CreateThread(Shutdown, NULL);
\r
352 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
355 // Hide the task bar button when minimized.
\r
356 // Event is sent when the frame is minimized or restored.
\r
357 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
358 // to get rid of the deprecated warning. Just ignore it.
\r
359 if (!event.Iconized())
\r
360 fClosedToTray = false;
\r
362 // Tray is not reliable on ubuntu 9.10 gnome
\r
363 fClosedToTray = false;
\r
365 if (fMinimizeToTray && event.Iconized())
\r
366 fClosedToTray = true;
\r
367 Show(!fClosedToTray);
\r
368 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
371 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
375 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
376 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
379 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
381 // Hidden columns not resizeable
\r
382 if (event.GetColumn() <= 1 && !fDebug)
\r
388 int CMainFrame::GetSortIndex(const string& strSort)
\r
393 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
394 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
395 // In the wx generic implementation, they store the list of items
\r
396 // in a vector, so indexed lookups are fast, but inserts are slower
\r
397 // the closer they are to the top.
\r
399 int high = m_listCtrl->GetItemCount();
\r
402 int mid = low + ((high - low) / 2);
\r
403 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
412 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
414 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
\r
415 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
\r
418 if (!fNew && nIndex == -1)
\r
420 string strHash = " " + hashKey.ToString();
\r
421 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
422 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
426 // fNew is for blind insert, only use if you're sure it's new
\r
427 if (fNew || nIndex == -1)
\r
429 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
433 // If sort key changed, must delete and reinsert to make it relocate
\r
434 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
\r
436 m_listCtrl->DeleteItem(nIndex);
\r
437 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
441 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
\r
442 m_listCtrl->SetItem(nIndex, 2, str2);
\r
443 m_listCtrl->SetItem(nIndex, 3, str3);
\r
444 m_listCtrl->SetItem(nIndex, 4, str4);
\r
445 m_listCtrl->SetItem(nIndex, 5, str5);
\r
446 m_listCtrl->SetItem(nIndex, 6, str6);
\r
447 m_listCtrl->SetItemData(nIndex, nData);
\r
450 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
452 long nData = *(long*)&hashKey;
\r
456 string strHash = " " + hashKey.ToString();
\r
457 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
458 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
462 m_listCtrl->DeleteItem(nIndex);
\r
464 return nIndex != -1;
\r
467 string FormatTxStatus(const CWalletTx& wtx)
\r
470 if (!wtx.IsFinal())
\r
472 if (wtx.nLockTime < 500000000)
\r
473 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
\r
475 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
\r
479 int nDepth = wtx.GetDepthInMainChain();
\r
480 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
481 return strprintf(_("%d/offline?"), nDepth);
\r
482 else if (nDepth < 6)
\r
483 return strprintf(_("%d/unconfirmed"), nDepth);
\r
485 return strprintf(_("%d confirmations"), nDepth);
\r
489 string SingleLine(const string& strIn)
\r
492 bool fOneSpace = false;
\r
493 foreach(int c, strIn)
\r
501 if (fOneSpace && !strOut.empty())
\r
510 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
512 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
513 int64 nCredit = wtx.GetCredit(true);
\r
514 int64 nDebit = wtx.GetDebit();
\r
515 int64 nNet = nCredit - nDebit;
\r
516 uint256 hash = wtx.GetHash();
\r
517 string strStatus = FormatTxStatus(wtx);
\r
518 map<string, string> mapValue = wtx.mapValue;
\r
519 wtx.nLinesDisplayed = 1;
\r
520 nListViewUpdated++;
\r
523 if (wtx.IsCoinBase())
\r
525 // Don't show generated coin until confirmed by at least one block after it
\r
526 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
528 // It is not an error when generated blocks are not accepted. By design,
\r
529 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
530 // This is the normal mechanism by which the network copes with latency.
\r
532 // We display regular transactions right away before any confirmation
\r
533 // because they can always get into some block eventually. Generated coins
\r
534 // are special because if their block is not accepted, they are not valid.
\r
536 if (wtx.GetDepthInMainChain() < 2)
\r
538 wtx.nLinesDisplayed = 0;
\r
542 if (!fShowGenerated)
\r
546 // Find the block the tx is in
\r
547 CBlockIndex* pindex = NULL;
\r
548 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
549 if (mi != mapBlockIndex.end())
\r
550 pindex = (*mi).second;
\r
552 // Sort order, unrecorded transactions sort to the top
\r
553 string strSort = strprintf("%010d-%01d-%010u",
\r
554 (pindex ? pindex->nHeight : INT_MAX),
\r
555 (wtx.IsCoinBase() ? 1 : 0),
\r
556 wtx.nTimeReceived);
\r
559 if (nNet > 0 || wtx.IsCoinBase())
\r
564 string strDescription;
\r
565 if (wtx.IsCoinBase())
\r
568 strDescription = _("Generated");
\r
571 int64 nUnmatured = 0;
\r
572 foreach(const CTxOut& txout, wtx.vout)
\r
573 nUnmatured += txout.GetCredit();
\r
574 if (wtx.IsInMainChain())
\r
576 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
578 // Check if the block was requested by anyone
\r
579 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
580 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
\r
584 strDescription = _("Generated (not accepted)");
\r
588 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
590 // Received by IP connection
\r
591 if (!fShowReceived)
\r
593 if (!mapValue["from"].empty())
\r
594 strDescription += _("From: ") + mapValue["from"];
\r
595 if (!mapValue["message"].empty())
\r
597 if (!strDescription.empty())
\r
598 strDescription += " - ";
\r
599 strDescription += mapValue["message"];
\r
604 // Received by Bitcoin Address
\r
605 if (!fShowReceived)
\r
607 foreach(const CTxOut& txout, wtx.vout)
\r
609 if (txout.IsMine())
\r
611 vector<unsigned char> vchPubKey;
\r
612 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
614 CRITICAL_BLOCK(cs_mapAddressBook)
\r
616 //strDescription += _("Received payment to ");
\r
617 //strDescription += _("Received with address ");
\r
618 strDescription += _("From: unknown, Received with: ");
\r
619 string strAddress = PubKeyToAddress(vchPubKey);
\r
620 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
\r
621 if (mi != mapAddressBook.end() && !(*mi).second.empty())
\r
623 string strLabel = (*mi).second;
\r
624 strDescription += strAddress.substr(0,12) + "... ";
\r
625 strDescription += "(" + strLabel + ")";
\r
628 strDescription += strAddress;
\r
636 InsertLine(fNew, nIndex, hash, strSort,
\r
638 nTime ? DateTimeStr(nTime) : "",
\r
639 SingleLine(strDescription),
\r
641 FormatMoney(nNet, true));
\r
645 bool fAllFromMe = true;
\r
646 foreach(const CTxIn& txin, wtx.vin)
\r
647 fAllFromMe = fAllFromMe && txin.IsMine();
\r
649 bool fAllToMe = true;
\r
650 foreach(const CTxOut& txout, wtx.vout)
\r
651 fAllToMe = fAllToMe && txout.IsMine();
\r
653 if (fAllFromMe && fAllToMe)
\r
656 int64 nValue = wtx.vout[0].nValue;
\r
657 InsertLine(fNew, nIndex, hash, strSort,
\r
659 nTime ? DateTimeStr(nTime) : "",
\r
660 _("Payment to yourself"),
\r
663 /// issue: can't tell which is the payment and which is the change anymore
\r
664 // FormatMoney(nNet - nValue, true),
\r
665 // FormatMoney(nValue, true));
\r
667 else if (fAllFromMe)
\r
675 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
676 wtx.nLinesDisplayed = 0;
\r
677 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
679 const CTxOut& txout = wtx.vout[nOut];
\r
680 if (txout.IsMine())
\r
684 if (!mapValue["to"].empty())
\r
687 strAddress = mapValue["to"];
\r
691 // Sent to Bitcoin Address
\r
693 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
694 strAddress = Hash160ToAddress(hash160);
\r
697 string strDescription = _("To: ");
\r
698 CRITICAL_BLOCK(cs_mapAddressBook)
\r
699 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
700 strDescription += mapAddressBook[strAddress] + " ";
\r
701 strDescription += strAddress;
\r
702 if (!mapValue["message"].empty())
\r
704 if (!strDescription.empty())
\r
705 strDescription += " - ";
\r
706 strDescription += mapValue["message"];
\r
709 int64 nValue = txout.nValue;
\r
716 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
718 nTime ? DateTimeStr(nTime) : "",
\r
719 SingleLine(strDescription),
\r
720 FormatMoney(-nValue, true),
\r
722 wtx.nLinesDisplayed++;
\r
728 // Mixed debit transaction, can't break down payees
\r
730 bool fAllMine = true;
\r
731 foreach(const CTxOut& txout, wtx.vout)
\r
732 fAllMine = fAllMine && txout.IsMine();
\r
733 foreach(const CTxIn& txin, wtx.vin)
\r
734 fAllMine = fAllMine && txin.IsMine();
\r
736 InsertLine(fNew, nIndex, hash, strSort,
\r
738 nTime ? DateTimeStr(nTime) : "",
\r
740 FormatMoney(nNet, true),
\r
748 void CMainFrame::RefreshListCtrl()
\r
750 fRefreshListCtrl = true;
\r
754 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
756 if (fRefreshListCtrl)
\r
758 // Collect list of wallet transactions and sort newest first
\r
759 bool fEntered = false;
\r
760 vector<pair<unsigned int, uint256> > vSorted;
\r
761 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
763 printf("RefreshListCtrl starting\n");
\r
765 fRefreshListCtrl = false;
\r
766 vWalletUpdated.clear();
\r
768 // Do the newest transactions first
\r
769 vSorted.reserve(mapWallet.size());
\r
770 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
772 const CWalletTx& wtx = (*it).second;
\r
773 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
774 vSorted.push_back(make_pair(nTime, (*it).first));
\r
776 m_listCtrl->DeleteAllItems();
\r
781 sort(vSorted.begin(), vSorted.end());
\r
783 // Fill list control
\r
784 for (int i = 0; i < vSorted.size();)
\r
788 bool fEntered = false;
\r
789 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
792 uint256& hash = vSorted[i++].second;
\r
793 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
794 if (mi != mapWallet.end())
\r
795 InsertTransaction((*mi).second, true);
\r
797 if (!fEntered || i == 100 || i % 500 == 0)
\r
801 printf("RefreshListCtrl done\n");
\r
803 // Update transaction total display
\r
804 MainFrameRepaint();
\r
808 // Check for time updates
\r
809 static int64 nLastTime;
\r
810 if (GetTime() > nLastTime + 30)
\r
812 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
814 nLastTime = GetTime();
\r
815 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
817 CWalletTx& wtx = (*it).second;
\r
818 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
819 InsertTransaction(wtx, false);
\r
826 void CMainFrame::RefreshStatusColumn()
\r
828 static int nLastTop;
\r
829 static CBlockIndex* pindexLastBest;
\r
830 static unsigned int nLastRefreshed;
\r
832 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
833 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
836 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
839 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
841 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
843 // If no updates, only need to do the part that moved onto the screen
\r
844 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
845 nStart = nLastTop + 100;
\r
846 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
850 pindexLastBest = pindexBest;
\r
851 nLastRefreshed = nListViewUpdated;
\r
853 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
855 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
856 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
857 if (mi == mapWallet.end())
\r
859 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
862 CWalletTx& wtx = (*mi).second;
\r
863 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
865 if (!InsertTransaction(wtx, false, nIndex))
\r
866 m_listCtrl->DeleteItem(nIndex--);
\r
869 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
874 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
885 unsigned int nNeedRepaint = 0;
\r
886 unsigned int nLastRepaint = 0;
\r
887 int64 nLastRepaintTime = 0;
\r
888 int64 nRepaintInterval = 500;
\r
890 void ThreadDelayedRepaint(void* parg)
\r
894 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
896 nLastRepaint = nNeedRepaint;
\r
899 printf("DelayedRepaint\n");
\r
900 wxPaintEvent event;
\r
901 pframeMain->fRefresh = true;
\r
902 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
905 Sleep(nRepaintInterval);
\r
909 void MainFrameRepaint()
\r
911 // This is called by network code that shouldn't access pframeMain
\r
912 // directly because it could still be running after the UI is closed.
\r
915 // Don't repaint too often
\r
916 static int64 nLastRepaintRequest;
\r
917 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
922 nLastRepaintRequest = GetTimeMillis();
\r
924 printf("MainFrameRepaint\n");
\r
925 wxPaintEvent event;
\r
926 pframeMain->fRefresh = true;
\r
927 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
931 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
933 // Skip lets the listctrl do the paint, we're just hooking the message
\r
937 ptaskbaricon->UpdateTooltip();
\r
942 static int nTransactionCount;
\r
943 bool fPaintedBalance = false;
\r
944 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
946 nLastRepaint = nNeedRepaint;
\r
947 nLastRepaintTime = GetTimeMillis();
\r
949 // Update listctrl contents
\r
950 if (!vWalletUpdated.empty())
\r
952 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
955 if (m_listCtrl->GetItemCount())
\r
956 strTop = (string)m_listCtrl->GetItemText(0);
\r
957 foreach(uint256 hash, vWalletUpdated)
\r
959 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
960 if (mi != mapWallet.end())
\r
961 InsertTransaction((*mi).second, false);
\r
963 vWalletUpdated.clear();
\r
964 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
965 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
970 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
972 fPaintedBalance = true;
\r
973 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
975 // Count hidden and multi-line transactions
\r
976 nTransactionCount = 0;
\r
977 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
979 CWalletTx& wtx = (*it).second;
\r
980 nTransactionCount += wtx.nLinesDisplayed;
\r
984 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
987 // Update status column of visible items only
\r
988 RefreshStatusColumn();
\r
990 // Update status bar
\r
991 string strGen = "";
\r
992 if (fGenerateBitcoins)
\r
993 strGen = _(" Generating");
\r
994 if (fGenerateBitcoins && vNodes.empty())
\r
995 strGen = _("(not connected)");
\r
996 m_statusBar->SetStatusText(strGen, 1);
\r
998 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
999 m_statusBar->SetStatusText(strStatus, 2);
\r
1001 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1002 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1006 void UIThreadCall(boost::function0<void> fn)
\r
1008 // Call this with a function object created with bind.
\r
1009 // bind needs all parameters to match the function's expected types
\r
1010 // and all default parameters specified. Some examples:
\r
1011 // UIThreadCall(bind(wxBell));
\r
1012 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1013 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1016 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1017 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1018 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1022 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1024 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1029 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1035 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1037 // Options->Generate Coins
\r
1038 GenerateBitcoins(event.IsChecked());
\r
1041 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1043 event.Check(fGenerateBitcoins);
\r
1046 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1048 // Options->Your Receiving Addresses
\r
1049 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
\r
1050 if (!dialog.ShowModal())
\r
1054 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1056 // Options->Options
\r
1057 COptionsDialog dialog(this);
\r
1058 dialog.ShowModal();
\r
1061 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1064 CAboutDialog dialog(this);
\r
1065 dialog.ShowModal();
\r
1068 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1071 CSendDialog dialog(this);
\r
1072 dialog.ShowModal();
\r
1075 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1077 // Toolbar: Address Book
\r
1078 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
\r
1079 if (dialogAddr.ShowModal() == 2)
\r
1082 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
\r
1083 dialogSend.ShowModal();
\r
1087 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1089 // Automatically select-all when entering window
\r
1091 m_textCtrlAddress->SetSelection(-1, -1);
\r
1092 fOnSetFocusAddress = true;
\r
1095 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1098 if (fOnSetFocusAddress)
\r
1099 m_textCtrlAddress->SetSelection(-1, -1);
\r
1100 fOnSetFocusAddress = false;
\r
1103 void CMainFrame::OnButtonNew(wxCommandEvent& event)
\r
1106 CGetTextFromUserDialog dialog(this,
\r
1107 _("New Receiving Address"),
\r
1108 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
\r
1110 if (!dialog.ShowModal())
\r
1112 string strName = dialog.GetValue();
\r
1114 // Generate new key
\r
1115 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
1118 SetAddressBookName(strAddress, strName);
\r
1119 SetDefaultReceivingAddress(strAddress);
\r
1122 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1124 // Copy address box to clipboard
\r
1125 if (wxTheClipboard->Open())
\r
1127 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1128 wxTheClipboard->Close();
\r
1132 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1134 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1136 CRITICAL_BLOCK(cs_mapWallet)
\r
1138 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1139 if (mi == mapWallet.end())
\r
1141 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1144 wtx = (*mi).second;
\r
1146 CTxDetailsDialog dialog(this, wtx);
\r
1147 dialog.ShowModal();
\r
1148 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1149 //pdialog->Show();
\r
1157 //////////////////////////////////////////////////////////////////////////////
\r
1159 // CTxDetailsDialog
\r
1162 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1164 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1167 strHTML.reserve(4000);
\r
1168 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1170 int64 nTime = wtx.GetTxTime();
\r
1171 int64 nCredit = wtx.GetCredit();
\r
1172 int64 nDebit = wtx.GetDebit();
\r
1173 int64 nNet = nCredit - nDebit;
\r
1177 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
\r
1178 int nRequests = wtx.GetRequestCount();
\r
1179 if (nRequests != -1)
\r
1181 if (nRequests == 0)
\r
1182 strHTML += _(", has not been successfully broadcast yet");
\r
1183 else if (nRequests == 1)
\r
1184 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
\r
1186 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
\r
1188 strHTML += "<br>";
\r
1190 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1196 if (wtx.IsCoinBase())
\r
1198 strHTML += _("<b>Source:</b> Generated<br>");
\r
1200 else if (!wtx.mapValue["from"].empty())
\r
1202 // Online transaction
\r
1203 if (!wtx.mapValue["from"].empty())
\r
1204 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1208 // Offline transaction
\r
1212 foreach(const CTxOut& txout, wtx.vout)
\r
1214 if (txout.IsMine())
\r
1216 vector<unsigned char> vchPubKey;
\r
1217 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1219 string strAddress = PubKeyToAddress(vchPubKey);
\r
1220 if (mapAddressBook.count(strAddress))
\r
1222 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
\r
1223 strHTML += _("<b>To:</b> ");
\r
1224 strHTML += HtmlEscape(strAddress);
\r
1225 if (!mapAddressBook[strAddress].empty())
\r
1226 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
\r
1228 strHTML += _(" (yours)");
\r
1229 strHTML += "<br>";
\r
1242 string strAddress;
\r
1243 if (!wtx.mapValue["to"].empty())
\r
1245 // Online transaction
\r
1246 strAddress = wtx.mapValue["to"];
\r
1247 strHTML += _("<b>To:</b> ");
\r
1248 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1249 strHTML += mapAddressBook[strAddress] + " ";
\r
1250 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1257 if (wtx.IsCoinBase() && nCredit == 0)
\r
1262 int64 nUnmatured = 0;
\r
1263 foreach(const CTxOut& txout, wtx.vout)
\r
1264 nUnmatured += txout.GetCredit();
\r
1265 strHTML += _("<b>Credit:</b> ");
\r
1266 if (wtx.IsInMainChain())
\r
1267 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1269 strHTML += _("(not accepted)");
\r
1270 strHTML += "<br>";
\r
1272 else if (nNet > 0)
\r
1277 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
\r
1281 bool fAllFromMe = true;
\r
1282 foreach(const CTxIn& txin, wtx.vin)
\r
1283 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1285 bool fAllToMe = true;
\r
1286 foreach(const CTxOut& txout, wtx.vout)
\r
1287 fAllToMe = fAllToMe && txout.IsMine();
\r
1294 foreach(const CTxOut& txout, wtx.vout)
\r
1296 if (txout.IsMine())
\r
1299 if (wtx.mapValue["to"].empty())
\r
1301 // Offline transaction
\r
1303 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1305 string strAddress = Hash160ToAddress(hash160);
\r
1306 strHTML += _("<b>To:</b> ");
\r
1307 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1308 strHTML += mapAddressBook[strAddress] + " ";
\r
1309 strHTML += strAddress;
\r
1310 strHTML += "<br>";
\r
1314 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
\r
1319 // Payment to self
\r
1320 /// issue: can't tell which is the payment and which is the change anymore
\r
1321 //int64 nValue = wtx.vout[0].nValue;
\r
1322 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
\r
1323 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
\r
1326 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1328 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
\r
1333 // Mixed debit transaction
\r
1335 foreach(const CTxIn& txin, wtx.vin)
\r
1336 if (txin.IsMine())
\r
1337 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1338 foreach(const CTxOut& txout, wtx.vout)
\r
1339 if (txout.IsMine())
\r
1340 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
\r
1344 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
\r
1350 if (!wtx.mapValue["message"].empty())
\r
1351 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1353 if (wtx.IsCoinBase())
\r
1354 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
1362 strHTML += "<hr><br>debug print<br><br>";
\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
1370 strHTML += "<b>Inputs:</b><br>";
\r
1371 CRITICAL_BLOCK(cs_mapWallet)
\r
1373 foreach(const CTxIn& txin, wtx.vin)
\r
1375 COutPoint prevout = txin.prevout;
\r
1376 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1377 if (mi != mapWallet.end())
\r
1379 const CWalletTx& prev = (*mi).second;
\r
1380 if (prevout.n < prev.vout.size())
\r
1382 strHTML += HtmlEscape(prev.ToString(), true);
\r
1383 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1384 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1390 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1391 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1396 strHTML += "</font></html>";
\r
1397 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1398 m_htmlWin->SetPage(strHTML);
\r
1399 m_buttonOK->SetFocus();
\r
1403 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1413 //////////////////////////////////////////////////////////////////////////////
\r
1418 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1420 // Set up list box of page choices
\r
1421 m_listBox->Append(_("Main"));
\r
1422 //m_listBox->Append(_("Test 2"));
\r
1423 m_listBox->SetSelection(0);
\r
1426 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
\r
1427 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1431 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1432 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1433 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1434 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1435 int nProcessors = wxThread::GetCPUCount();
\r
1436 if (nProcessors < 1)
\r
1437 nProcessors = 999;
\r
1438 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1439 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1440 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1441 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1442 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1443 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1444 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1445 m_staticTextProxyIP->Enable(fUseProxy);
\r
1446 m_staticTextProxyPort->Enable(fUseProxy);
\r
1447 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1448 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1450 m_buttonOK->SetFocus();
\r
1453 void COptionsDialog::SelectPage(int nPage)
\r
1455 m_panelMain->Show(nPage == 0);
\r
1456 m_panelTest2->Show(nPage == 1);
\r
1458 m_scrolledWindow->Layout();
\r
1459 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1462 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1464 SelectPage(event.GetSelection());
\r
1467 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1470 int64 nTmp = nTransactionFee;
\r
1471 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1472 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1475 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1477 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1480 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1482 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1483 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1484 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1485 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1488 CAddress COptionsDialog::GetProxyAddr()
\r
1490 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1491 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1492 if (addr.ip == INADDR_NONE)
\r
1493 addr.ip = addrProxy.ip;
\r
1494 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1495 addr.port = htons(nPort);
\r
1496 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1497 addr.port = addrProxy.port;
\r
1501 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1504 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1505 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1509 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1511 OnButtonApply(event);
\r
1515 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1520 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1522 CWalletDB walletdb;
\r
1524 int64 nPrevTransactionFee = nTransactionFee;
\r
1525 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1526 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1528 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1529 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1531 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1532 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1534 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1536 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1537 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1539 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1540 GenerateBitcoins(fGenerateBitcoins);
\r
1542 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1544 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1545 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1548 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1550 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1551 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1552 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1555 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1557 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1558 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1561 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1562 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1564 addrProxy = GetProxyAddr();
\r
1565 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1572 //////////////////////////////////////////////////////////////////////////////
\r
1577 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1579 m_staticTextVersion->SetLabel(strprintf(_("version 0.%d.%d beta"), VERSION/100, VERSION%100));
\r
1581 // Change (c) into UTF-8 or ANSI copyright symbol
\r
1582 wxString str = m_staticTextMain->GetLabel();
\r
1584 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
\r
1586 str.Replace("(c)", "\xA9");
\r
1588 m_staticTextMain->SetLabel(str);
\r
1590 // Resize on Linux to make the window fit the text.
\r
1591 // The text was wrapped manually rather than using the Wrap setting because
\r
1592 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1593 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1594 if (fontTmp.GetPointSize() > 8);
\r
1595 fontTmp.SetPointSize(8);
\r
1596 m_staticTextMain->SetFont(fontTmp);
\r
1597 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
\r
1601 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1611 //////////////////////////////////////////////////////////////////////////////
\r
1616 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1619 m_textCtrlAddress->SetValue(strAddress);
\r
1620 m_choiceTransferType->SetSelection(0);
\r
1621 m_bitmapCheckMark->Show(false);
\r
1622 fEnabledPrev = true;
\r
1623 m_textCtrlAddress->SetFocus();
\r
1624 //// todo: should add a display of your balance for convenience
\r
1626 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1627 if (fontTmp.GetPointSize() > 9);
\r
1628 fontTmp.SetPointSize(9);
\r
1629 m_staticTextInstructions->SetFont(fontTmp);
\r
1630 SetSize(725, 380);
\r
1635 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1636 SetIcon(iconSend);
\r
1638 wxCommandEvent event;
\r
1639 OnTextAddress(event);
\r
1641 // Fixup the tab order
\r
1642 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1643 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1647 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1651 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1652 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1654 // Grey out message if bitcoin address
\r
1655 bool fEnable = !fBitcoinAddress;
\r
1656 m_staticTextFrom->Enable(fEnable);
\r
1657 m_textCtrlFrom->Enable(fEnable);
\r
1658 m_staticTextMessage->Enable(fEnable);
\r
1659 m_textCtrlMessage->Enable(fEnable);
\r
1660 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1661 if (!fEnable && fEnabledPrev)
\r
1663 strFromSave = m_textCtrlFrom->GetValue();
\r
1664 strMessageSave = m_textCtrlMessage->GetValue();
\r
1665 m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
\r
1666 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
\r
1668 else if (fEnable && !fEnabledPrev)
\r
1670 m_textCtrlFrom->SetValue(strFromSave);
\r
1671 m_textCtrlMessage->SetValue(strMessageSave);
\r
1673 fEnabledPrev = fEnable;
\r
1676 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1678 // Reformat the amount
\r
1680 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1683 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1684 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1687 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1689 // Open address book
\r
1690 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
\r
1691 if (dialog.ShowModal())
\r
1692 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
\r
1695 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1697 // Copy clipboard to address box
\r
1698 if (wxTheClipboard->Open())
\r
1700 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1702 wxTextDataObject data;
\r
1703 wxTheClipboard->GetData(data);
\r
1704 m_textCtrlAddress->SetValue(data.GetText());
\r
1706 wxTheClipboard->Close();
\r
1710 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1713 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1717 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1719 wxMessageBox(_("Error in amount "), _("Send Coins"));
\r
1722 if (nValue > GetBalance())
\r
1724 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
\r
1727 if (nValue + nTransactionFee > GetBalance())
\r
1729 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
\r
1733 // Parse bitcoin address
\r
1735 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1737 if (fBitcoinAddress)
\r
1739 // Send to bitcoin address
\r
1740 CScript scriptPubKey;
\r
1741 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1743 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
\r
1744 if (strError == "")
\r
1745 wxMessageBox(_("Payment sent "), _("Sending..."));
\r
1746 else if (strError != "ABORTED")
\r
1747 wxMessageBox(strError + " ", _("Sending..."));
\r
1751 // Parse IP address
\r
1752 CAddress addr(strAddress);
\r
1753 if (!addr.IsValid())
\r
1755 wxMessageBox(_("Invalid address "), _("Send Coins"));
\r
1760 wtx.mapValue["to"] = strAddress;
\r
1761 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1762 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1764 // Send to IP address
\r
1765 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1766 if (!pdialog->ShowModal())
\r
1770 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1771 if (!mapAddressBook.count(strAddress))
\r
1772 SetAddressBookName(strAddress, "");
\r
1777 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1788 //////////////////////////////////////////////////////////////////////////////
\r
1793 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
1796 nPrice = nPriceIn;
\r
1798 start = wxDateTime::UNow();
\r
1799 memset(pszStatus, 0, sizeof(pszStatus));
\r
1800 fCanCancel = true;
\r
1804 fWorkDone = false;
\r
1806 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
\r
1809 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1810 m_textCtrlStatus->SetValue("");
\r
1812 CreateThread(SendingDialogStartTransfer, this);
\r
1815 CSendingDialog::~CSendingDialog()
\r
1817 printf("~CSendingDialog()\n");
\r
1820 void CSendingDialog::Close()
\r
1822 // Last one out turn out the lights.
\r
1823 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1824 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1825 // This allows the window to disappear and end modality when cancelled
\r
1826 // without making the user wait for ConnectNode to return. The dialog object
\r
1827 // hangs around in the background until the work thread exits.
\r
1829 EndModal(fSuccess);
\r
1838 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1840 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1847 wxCommandEvent cmdevent;
\r
1848 OnButtonCancel(cmdevent);
\r
1852 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1858 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1864 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1867 if (strlen(pszStatus) > 130)
\r
1868 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1870 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1871 m_staticTextSending->SetFocus();
\r
1873 m_buttonCancel->Enable(false);
\r
1876 m_buttonOK->Enable(true);
\r
1877 m_buttonOK->SetFocus();
\r
1878 m_buttonCancel->Enable(false);
\r
1880 if (fAbort && fCanCancel && IsShown())
\r
1882 strcpy(pszStatus, _("CANCELLED"));
\r
1883 m_buttonOK->Enable(true);
\r
1884 m_buttonOK->SetFocus();
\r
1885 m_buttonCancel->Enable(false);
\r
1886 m_buttonCancel->SetLabel(_("Cancelled"));
\r
1888 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
\r
1894 // Everything from here on is not in the UI thread and must only communicate
\r
1895 // with the rest of the dialog through variables and calling repaint.
\r
1898 void CSendingDialog::Repaint()
\r
1901 wxPaintEvent event;
\r
1902 GetEventHandler()->AddPendingEvent(event);
\r
1905 bool CSendingDialog::Status()
\r
1912 if (fAbort && fCanCancel)
\r
1914 memset(pszStatus, 0, 10);
\r
1915 strcpy(pszStatus, _("CANCELLED"));
\r
1923 bool CSendingDialog::Status(const string& str)
\r
1928 // This can be read by the UI thread at any time,
\r
1929 // so copy in a way that can be read cleanly at all times.
\r
1930 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
1931 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
1937 bool CSendingDialog::Error(const string& str)
\r
1939 fCanCancel = false;
\r
1941 Status(string(_("Error: ")) + str);
\r
1945 void SendingDialogStartTransfer(void* parg)
\r
1947 ((CSendingDialog*)parg)->StartTransfer();
\r
1950 void CSendingDialog::StartTransfer()
\r
1952 // Make sure we have enough money
\r
1953 if (nPrice + nTransactionFee > GetBalance())
\r
1955 Error(_("Insufficient funds"));
\r
1959 // We may have connected already for product details
\r
1960 if (!Status(_("Connecting...")))
\r
1962 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
1965 Error(_("Unable to connect"));
\r
1969 // Send order to seller, with response going to OnReply2 via event handler
\r
1970 if (!Status(_("Requesting public key...")))
\r
1972 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
1975 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
1977 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
1980 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
1982 if (!Status(_("Received public key...")))
\r
1985 CScript scriptPubKey;
\r
1992 string strMessage;
\r
1993 vRecv >> strMessage;
\r
1994 Error(_("Transfer was not accepted"));
\r
1995 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
1998 vRecv >> scriptPubKey;
\r
2002 //// what do we want to do about this?
\r
2003 Error(_("Invalid response received"));
\r
2007 // Pause to give the user a chance to cancel
\r
2008 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2015 CRITICAL_BLOCK(cs_main)
\r
2018 if (!Status(_("Creating transaction...")))
\r
2020 if (nPrice + nTransactionFee > GetBalance())
\r
2022 Error(_("Insufficient funds"));
\r
2026 int64 nFeeRequired;
\r
2027 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2029 if (nPrice + nFeeRequired > GetBalance())
\r
2030 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
\r
2032 Error(_("Transaction creation failed"));
\r
2036 // Transaction fee
\r
2037 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
\r
2039 Error(_("Transaction aborted"));
\r
2043 // Make sure we're still connected
\r
2044 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2047 Error(_("Lost connection, transaction cancelled"));
\r
2051 // Last chance to cancel
\r
2055 fCanCancel = false;
\r
2058 fCanCancel = true;
\r
2061 fCanCancel = false;
\r
2063 if (!Status(_("Sending payment...")))
\r
2067 if (!CommitTransaction(wtx, key))
\r
2069 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
2073 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2074 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\r
2076 Status(_("Waiting for confirmation..."));
\r
2077 MainFrameRepaint();
\r
2081 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2083 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2086 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2094 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
\r
2095 "The transaction is recorded and will credit to the recipient,\n"
\r
2096 "but the comment information will be blank."));
\r
2102 //// what do we want to do about this?
\r
2103 Error(_("Payment was sent, but an invalid response was received"));
\r
2109 Status(_("Payment completed"));
\r
2117 //////////////////////////////////////////////////////////////////////////////
\r
2119 // CAddressBookDialog
\r
2122 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
\r
2124 // Set initially selected page
\r
2125 wxNotebookEvent event;
\r
2126 event.SetSelection(nPageIn);
\r
2127 OnNotebookPageChanged(event);
\r
2128 m_notebook->ChangeSelection(nPageIn);
\r
2130 fDuringSend = fDuringSendIn;
\r
2132 m_buttonCancel->Show(false);
\r
2135 wxIcon iconAddressBook;
\r
2136 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2137 SetIcon(iconAddressBook);
\r
2139 // Init column headers
\r
2140 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
\r
2141 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
\r
2142 m_listCtrlSending->SetFocus();
\r
2143 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
\r
2144 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
\r
2145 m_listCtrlReceiving->SetFocus();
\r
2147 // Fill listctrl with address book data
\r
2148 CRITICAL_BLOCK(cs_mapKeys)
\r
2149 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2151 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
\r
2152 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2154 string strAddress = item.first;
\r
2155 string strName = item.second;
\r
2157 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2158 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
\r
2159 int nIndex = InsertLine(plistCtrl, strName, strAddress);
\r
2160 if (strAddress == (fMine ? strDefaultReceiving : strInitSelected))
\r
2161 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2166 wxString CAddressBookDialog::GetSelectedAddress()
\r
2168 int nIndex = GetSelection(m_listCtrl);
\r
2171 return GetItemText(m_listCtrl, nIndex, 1);
\r
2174 wxString CAddressBookDialog::GetSelectedSendingAddress()
\r
2176 int nIndex = GetSelection(m_listCtrlSending);
\r
2179 return GetItemText(m_listCtrlSending, nIndex, 1);
\r
2182 wxString CAddressBookDialog::GetSelectedReceivingAddress()
\r
2184 int nIndex = GetSelection(m_listCtrlReceiving);
\r
2187 return GetItemText(m_listCtrlReceiving, nIndex, 1);
\r
2190 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
\r
2193 nPage = event.GetSelection();
\r
2194 if (nPage == SENDING)
\r
2195 m_listCtrl = m_listCtrlSending;
\r
2196 else if (nPage == RECEIVING)
\r
2197 m_listCtrl = m_listCtrlReceiving;
\r
2198 m_buttonDelete->Show(nPage == SENDING);
\r
2199 m_buttonCopy->Show(nPage == RECEIVING);
\r
2201 m_listCtrl->SetFocus();
\r
2204 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2206 // Update address book with edited name
\r
2208 if (event.IsEditCancelled())
\r
2210 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2211 SetAddressBookName(strAddress, string(event.GetText()));
\r
2212 pframeMain->RefreshListCtrl();
\r
2215 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2218 if (nPage == RECEIVING)
\r
2219 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
\r
2222 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2227 // Doubleclick returns selection
\r
2228 EndModal(GetSelectedAddress() != "" ? 2 : 0);
\r
2232 // Doubleclick edits item
\r
2233 wxCommandEvent event2;
\r
2234 OnButtonEdit(event2);
\r
2237 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2239 if (nPage != SENDING)
\r
2241 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2243 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2245 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2246 CWalletDB().EraseName(strAddress);
\r
2247 m_listCtrl->DeleteItem(nIndex);
\r
2250 pframeMain->RefreshListCtrl();
\r
2253 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2255 // Copy address box to clipboard
\r
2256 if (wxTheClipboard->Open())
\r
2258 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
\r
2259 wxTheClipboard->Close();
\r
2263 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2266 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2268 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
\r
2272 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2274 int nIndex = GetSelection(m_listCtrl);
\r
2277 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2278 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2279 string strAddressOrg = strAddress;
\r
2281 if (nPage == SENDING)
\r
2283 // Ask name and address
\r
2286 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
\r
2287 if (!dialog.ShowModal())
\r
2289 strName = dialog.GetValue1();
\r
2290 strAddress = dialog.GetValue2();
\r
2292 while (CheckIfMine(strAddress, _("Edit Address")));
\r
2295 else if (nPage == RECEIVING)
\r
2298 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
\r
2299 if (!dialog.ShowModal())
\r
2301 strName = dialog.GetValue();
\r
2305 if (strAddress != strAddressOrg)
\r
2306 CWalletDB().EraseName(strAddressOrg);
\r
2307 SetAddressBookName(strAddress, strName);
\r
2308 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2309 m_listCtrl->SetItemText(nIndex, strName);
\r
2310 pframeMain->RefreshListCtrl();
\r
2313 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2316 string strAddress;
\r
2318 if (nPage == SENDING)
\r
2320 // Ask name and address
\r
2323 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
\r
2324 if (!dialog.ShowModal())
\r
2326 strName = dialog.GetValue1();
\r
2327 strAddress = dialog.GetValue2();
\r
2329 while (CheckIfMine(strAddress, _("Add Address")));
\r
2331 else if (nPage == RECEIVING)
\r
2334 CGetTextFromUserDialog dialog(this,
\r
2335 _("New Receiving Address"),
\r
2336 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
\r
2338 if (!dialog.ShowModal())
\r
2340 strName = dialog.GetValue();
\r
2342 // Generate new key
\r
2343 strAddress = PubKeyToAddress(GenerateNewKey());
\r
2346 // Add to list and select it
\r
2347 SetAddressBookName(strAddress, strName);
\r
2348 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2349 SetSelection(m_listCtrl, nIndex);
\r
2350 m_listCtrl->SetFocus();
\r
2351 if (nPage == SENDING)
\r
2352 pframeMain->RefreshListCtrl();
\r
2355 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2358 EndModal(GetSelectedAddress() != "" ? 1 : 0);
\r
2361 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2367 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2378 //////////////////////////////////////////////////////////////////////////////
\r
2385 ID_TASKBAR_RESTORE = 10001,
\r
2386 ID_TASKBAR_OPTIONS,
\r
2387 ID_TASKBAR_GENERATE,
\r
2391 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
2392 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
2393 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
2394 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
2395 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
2396 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
2397 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
2400 void CMyTaskBarIcon::Show(bool fShow)
\r
2402 static char pszPrevTip[200];
\r
2405 string strTooltip = _("Bitcoin");
\r
2406 if (fGenerateBitcoins)
\r
2407 strTooltip = _("Bitcoin - Generating");
\r
2408 if (fGenerateBitcoins && vNodes.empty())
\r
2409 strTooltip = _("Bitcoin - (not connected)");
\r
2411 // Optimization, only update when changed, using char array to be reentrant
\r
2412 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
2414 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
2416 SetIcon(wxICON(bitcoin), strTooltip);
\r
2418 SetIcon(bitcoin20_xpm, strTooltip);
\r
2424 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
2429 void CMyTaskBarIcon::Hide()
\r
2434 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
2439 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
2444 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
2446 // Since it's modal, get the main window to do it
\r
2447 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);
\r
2448 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
2451 void CMyTaskBarIcon::Restore()
\r
2453 pframeMain->Show();
\r
2454 wxIconizeEvent event(0, false);
\r
2455 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
2456 pframeMain->Iconize(false);
\r
2457 pframeMain->Raise();
\r
2460 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
2462 GenerateBitcoins(event.IsChecked());
\r
2465 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
2467 event.Check(fGenerateBitcoins);
\r
2470 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
2472 pframeMain->Close(true);
\r
2475 void CMyTaskBarIcon::UpdateTooltip()
\r
2477 if (IsIconInstalled())
\r
2481 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
2483 wxMenu* pmenu = new wxMenu;
\r
2484 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
\r
2485 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
\r
2486 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
\r
2487 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
2488 pmenu->AppendSeparator();
\r
2489 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
\r
2506 void CreateMainWindow()
\r
2508 pframeMain = new CMainFrame(NULL);
\r
2509 if (mapArgs.count("-min"))
\r
2510 pframeMain->Iconize(true);
\r
2511 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
2512 if (fMinimizeToTray && pframeMain->IsIconized())
\r
2513 fClosedToTray = true;
\r
2514 pframeMain->Show(!fClosedToTray);
\r
2515 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
2516 CreateThread(ThreadDelayedRepaint, NULL);
\r