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 CalledSetStatusBar(const string& strText, int nField)
\r
198 if (pframeMain && pframeMain->m_statusBar)
\r
199 pframeMain->m_statusBar->SetStatusText(strText, nField);
\r
202 void SetDefaultReceivingAddress(const string& strAddress)
\r
204 // Update main window address and database
\r
205 if (pframeMain == NULL)
\r
207 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
\r
210 if (!AddressToHash160(strAddress, hash160))
\r
212 if (!mapPubKeys.count(hash160))
\r
214 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
215 pframeMain->m_textCtrlAddress->SetValue(strAddress);
\r
228 //////////////////////////////////////////////////////////////////////////////
\r
233 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
235 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
237 // Set initially selected page
\r
238 wxNotebookEvent event;
\r
239 event.SetSelection(0);
\r
240 OnNotebookPageChanged(event);
\r
241 m_notebook->ChangeSelection(0);
\r
244 fRefreshListCtrl = false;
\r
245 fRefreshListCtrlRunning = false;
\r
246 fOnSetFocusAddress = false;
\r
248 m_choiceFilter->SetSelection(0);
\r
249 double dResize = 1.0;
\r
251 SetIcon(wxICON(bitcoin));
\r
253 SetIcon(bitcoin80_xpm);
\r
254 SetBackgroundColour(m_toolBar->GetBackgroundColour());
\r
255 wxFont fontTmp = m_staticText41->GetFont();
\r
256 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
257 m_staticTextBalance->SetFont(fontTmp);
\r
258 m_staticTextBalance->SetSize(140, 17);
\r
259 // resize to fit ubuntu's huge default font
\r
261 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
\r
263 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
264 m_listCtrl->SetFocus();
\r
265 ptaskbaricon = new CMyTaskBarIcon();
\r
267 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
\r
268 // to their standard places, leaving these menus empty.
\r
269 GetMenuBar()->Remove(2); // remove Help menu
\r
270 GetMenuBar()->Remove(0); // remove File menu
\r
273 // Init column headers
\r
274 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
275 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
281 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
\r
282 foreach(wxListCtrl* p, pplistCtrl)
\r
284 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
285 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
286 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
\r
287 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
288 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
289 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
290 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
\r
294 int pnWidths[3] = { -100, 88, 300 };
\r
296 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
297 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
299 m_statusBar->SetFieldsCount(3, pnWidths);
\r
301 // Fill your address text box
\r
302 vector<unsigned char> vchPubKey;
\r
303 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
304 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
306 // Fill listctrl with wallet transactions
\r
310 CMainFrame::~CMainFrame()
\r
313 delete ptaskbaricon;
\r
314 ptaskbaricon = NULL;
\r
317 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
\r
320 nPage = event.GetSelection();
\r
323 m_listCtrl = m_listCtrlAll;
\r
324 fShowGenerated = true;
\r
326 fShowReceived = true;
\r
328 else if (nPage == SENTRECEIVED)
\r
330 m_listCtrl = m_listCtrlSentReceived;
\r
331 fShowGenerated = false;
\r
333 fShowReceived = true;
\r
335 else if (nPage == SENT)
\r
337 m_listCtrl = m_listCtrlSent;
\r
338 fShowGenerated = false;
\r
340 fShowReceived = false;
\r
342 else if (nPage == RECEIVED)
\r
344 m_listCtrl = m_listCtrlReceived;
\r
345 fShowGenerated = false;
\r
347 fShowReceived = true;
\r
350 m_listCtrl->SetFocus();
\r
353 void CMainFrame::OnClose(wxCloseEvent& event)
\r
355 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
357 // Divert close to minimize
\r
359 fClosedToTray = true;
\r
365 CreateThread(Shutdown, NULL);
\r
369 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
372 // Hide the task bar button when minimized.
\r
373 // Event is sent when the frame is minimized or restored.
\r
374 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
375 // to get rid of the deprecated warning. Just ignore it.
\r
376 if (!event.Iconized())
\r
377 fClosedToTray = false;
\r
379 // The tray icon sometimes disappears on ubuntu karmic
\r
380 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
\r
381 if (fMinimizeToTray && event.Iconized())
\r
382 fClosedToTray = true;
\r
383 Show(!fClosedToTray);
\r
385 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
388 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
392 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
393 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
396 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
398 // Hidden columns not resizeable
\r
399 if (event.GetColumn() <= 1 && !fDebug)
\r
405 int CMainFrame::GetSortIndex(const string& strSort)
\r
410 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
411 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
412 // In the wx generic implementation, they store the list of items
\r
413 // in a vector, so indexed lookups are fast, but inserts are slower
\r
414 // the closer they are to the top.
\r
416 int high = m_listCtrl->GetItemCount();
\r
419 int mid = low + ((high - low) / 2);
\r
420 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
429 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
431 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
\r
432 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
\r
435 if (!fNew && nIndex == -1)
\r
437 string strHash = " " + hashKey.ToString();
\r
438 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
439 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
443 // fNew is for blind insert, only use if you're sure it's new
\r
444 if (fNew || nIndex == -1)
\r
446 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
450 // If sort key changed, must delete and reinsert to make it relocate
\r
451 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
\r
453 m_listCtrl->DeleteItem(nIndex);
\r
454 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
458 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
\r
459 m_listCtrl->SetItem(nIndex, 2, str2);
\r
460 m_listCtrl->SetItem(nIndex, 3, str3);
\r
461 m_listCtrl->SetItem(nIndex, 4, str4);
\r
462 m_listCtrl->SetItem(nIndex, 5, str5);
\r
463 m_listCtrl->SetItem(nIndex, 6, str6);
\r
464 m_listCtrl->SetItemData(nIndex, nData);
\r
467 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
469 long nData = *(long*)&hashKey;
\r
473 string strHash = " " + hashKey.ToString();
\r
474 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
475 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
479 m_listCtrl->DeleteItem(nIndex);
\r
481 return nIndex != -1;
\r
484 string FormatTxStatus(const CWalletTx& wtx)
\r
487 if (!wtx.IsFinal())
\r
489 if (wtx.nLockTime < 500000000)
\r
490 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
\r
492 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
\r
496 int nDepth = wtx.GetDepthInMainChain();
\r
497 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
498 return strprintf(_("%d/offline?"), nDepth);
\r
499 else if (nDepth < 6)
\r
500 return strprintf(_("%d/unconfirmed"), nDepth);
\r
502 return strprintf(_("%d confirmations"), nDepth);
\r
506 string SingleLine(const string& strIn)
\r
509 bool fOneSpace = false;
\r
510 foreach(int c, strIn)
\r
518 if (fOneSpace && !strOut.empty())
\r
527 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
529 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
530 int64 nCredit = wtx.GetCredit(true);
\r
531 int64 nDebit = wtx.GetDebit();
\r
532 int64 nNet = nCredit - nDebit;
\r
533 uint256 hash = wtx.GetHash();
\r
534 string strStatus = FormatTxStatus(wtx);
\r
535 map<string, string> mapValue = wtx.mapValue;
\r
536 wtx.nLinesDisplayed = 1;
\r
537 nListViewUpdated++;
\r
540 if (wtx.IsCoinBase())
\r
542 // Don't show generated coin until confirmed by at least one block after it
\r
543 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
545 // It is not an error when generated blocks are not accepted. By design,
\r
546 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
547 // This is the normal mechanism by which the network copes with latency.
\r
549 // We display regular transactions right away before any confirmation
\r
550 // because they can always get into some block eventually. Generated coins
\r
551 // are special because if their block is not accepted, they are not valid.
\r
553 if (wtx.GetDepthInMainChain() < 2)
\r
555 wtx.nLinesDisplayed = 0;
\r
559 if (!fShowGenerated)
\r
563 // Find the block the tx is in
\r
564 CBlockIndex* pindex = NULL;
\r
565 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
566 if (mi != mapBlockIndex.end())
\r
567 pindex = (*mi).second;
\r
569 // Sort order, unrecorded transactions sort to the top
\r
570 string strSort = strprintf("%010d-%01d-%010u",
\r
571 (pindex ? pindex->nHeight : INT_MAX),
\r
572 (wtx.IsCoinBase() ? 1 : 0),
\r
573 wtx.nTimeReceived);
\r
576 if (nNet > 0 || wtx.IsCoinBase())
\r
581 string strDescription;
\r
582 if (wtx.IsCoinBase())
\r
585 strDescription = _("Generated");
\r
588 int64 nUnmatured = 0;
\r
589 foreach(const CTxOut& txout, wtx.vout)
\r
590 nUnmatured += txout.GetCredit();
\r
591 if (wtx.IsInMainChain())
\r
593 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
595 // Check if the block was requested by anyone
\r
596 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
597 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
\r
601 strDescription = _("Generated (not accepted)");
\r
605 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
607 // Received by IP connection
\r
608 if (!fShowReceived)
\r
610 if (!mapValue["from"].empty())
\r
611 strDescription += _("From: ") + mapValue["from"];
\r
612 if (!mapValue["message"].empty())
\r
614 if (!strDescription.empty())
\r
615 strDescription += " - ";
\r
616 strDescription += mapValue["message"];
\r
621 // Received by Bitcoin Address
\r
622 if (!fShowReceived)
\r
624 foreach(const CTxOut& txout, wtx.vout)
\r
626 if (txout.IsMine())
\r
628 vector<unsigned char> vchPubKey;
\r
629 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
631 CRITICAL_BLOCK(cs_mapAddressBook)
\r
633 //strDescription += _("Received payment to ");
\r
634 //strDescription += _("Received with address ");
\r
635 strDescription += _("From: unknown, Received with: ");
\r
636 string strAddress = PubKeyToAddress(vchPubKey);
\r
637 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
\r
638 if (mi != mapAddressBook.end() && !(*mi).second.empty())
\r
640 string strLabel = (*mi).second;
\r
641 strDescription += strAddress.substr(0,12) + "... ";
\r
642 strDescription += "(" + strLabel + ")";
\r
645 strDescription += strAddress;
\r
653 InsertLine(fNew, nIndex, hash, strSort,
\r
655 nTime ? DateTimeStr(nTime) : "",
\r
656 SingleLine(strDescription),
\r
658 FormatMoney(nNet, true));
\r
662 bool fAllFromMe = true;
\r
663 foreach(const CTxIn& txin, wtx.vin)
\r
664 fAllFromMe = fAllFromMe && txin.IsMine();
\r
666 bool fAllToMe = true;
\r
667 foreach(const CTxOut& txout, wtx.vout)
\r
668 fAllToMe = fAllToMe && txout.IsMine();
\r
670 if (fAllFromMe && fAllToMe)
\r
673 int64 nValue = wtx.vout[0].nValue;
\r
674 InsertLine(fNew, nIndex, hash, strSort,
\r
676 nTime ? DateTimeStr(nTime) : "",
\r
677 _("Payment to yourself"),
\r
680 /// issue: can't tell which is the payment and which is the change anymore
\r
681 // FormatMoney(nNet - nValue, true),
\r
682 // FormatMoney(nValue, true));
\r
684 else if (fAllFromMe)
\r
692 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
693 wtx.nLinesDisplayed = 0;
\r
694 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
696 const CTxOut& txout = wtx.vout[nOut];
\r
697 if (txout.IsMine())
\r
701 if (!mapValue["to"].empty())
\r
704 strAddress = mapValue["to"];
\r
708 // Sent to Bitcoin Address
\r
710 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
711 strAddress = Hash160ToAddress(hash160);
\r
714 string strDescription = _("To: ");
\r
715 CRITICAL_BLOCK(cs_mapAddressBook)
\r
716 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
717 strDescription += mapAddressBook[strAddress] + " ";
\r
718 strDescription += strAddress;
\r
719 if (!mapValue["message"].empty())
\r
721 if (!strDescription.empty())
\r
722 strDescription += " - ";
\r
723 strDescription += mapValue["message"];
\r
726 int64 nValue = txout.nValue;
\r
733 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
735 nTime ? DateTimeStr(nTime) : "",
\r
736 SingleLine(strDescription),
\r
737 FormatMoney(-nValue, true),
\r
739 wtx.nLinesDisplayed++;
\r
745 // Mixed debit transaction, can't break down payees
\r
747 bool fAllMine = true;
\r
748 foreach(const CTxOut& txout, wtx.vout)
\r
749 fAllMine = fAllMine && txout.IsMine();
\r
750 foreach(const CTxIn& txin, wtx.vin)
\r
751 fAllMine = fAllMine && txin.IsMine();
\r
753 InsertLine(fNew, nIndex, hash, strSort,
\r
755 nTime ? DateTimeStr(nTime) : "",
\r
757 FormatMoney(nNet, true),
\r
765 void CMainFrame::RefreshListCtrl()
\r
767 fRefreshListCtrl = true;
\r
771 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
773 if (fRefreshListCtrl)
\r
775 // Collect list of wallet transactions and sort newest first
\r
776 bool fEntered = false;
\r
777 vector<pair<unsigned int, uint256> > vSorted;
\r
778 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
780 printf("RefreshListCtrl starting\n");
\r
782 fRefreshListCtrl = false;
\r
783 vWalletUpdated.clear();
\r
785 // Do the newest transactions first
\r
786 vSorted.reserve(mapWallet.size());
\r
787 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
789 const CWalletTx& wtx = (*it).second;
\r
790 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
791 vSorted.push_back(make_pair(nTime, (*it).first));
\r
793 m_listCtrl->DeleteAllItems();
\r
798 sort(vSorted.begin(), vSorted.end());
\r
800 // Fill list control
\r
801 for (int i = 0; i < vSorted.size();)
\r
805 bool fEntered = false;
\r
806 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
809 uint256& hash = vSorted[i++].second;
\r
810 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
811 if (mi != mapWallet.end())
\r
812 InsertTransaction((*mi).second, true);
\r
814 if (!fEntered || i == 100 || i % 500 == 0)
\r
818 printf("RefreshListCtrl done\n");
\r
820 // Update transaction total display
\r
821 MainFrameRepaint();
\r
825 // Check for time updates
\r
826 static int64 nLastTime;
\r
827 if (GetTime() > nLastTime + 30)
\r
829 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
831 nLastTime = GetTime();
\r
832 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
834 CWalletTx& wtx = (*it).second;
\r
835 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
836 InsertTransaction(wtx, false);
\r
843 void CMainFrame::RefreshStatusColumn()
\r
845 static int nLastTop;
\r
846 static CBlockIndex* pindexLastBest;
\r
847 static unsigned int nLastRefreshed;
\r
849 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
850 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
853 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
856 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
858 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
860 // If no updates, only need to do the part that moved onto the screen
\r
861 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
862 nStart = nLastTop + 100;
\r
863 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
867 pindexLastBest = pindexBest;
\r
868 nLastRefreshed = nListViewUpdated;
\r
870 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
872 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
873 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
874 if (mi == mapWallet.end())
\r
876 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
879 CWalletTx& wtx = (*mi).second;
\r
880 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
882 if (!InsertTransaction(wtx, false, nIndex))
\r
883 m_listCtrl->DeleteItem(nIndex--);
\r
886 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
891 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
902 unsigned int nNeedRepaint = 0;
\r
903 unsigned int nLastRepaint = 0;
\r
904 int64 nLastRepaintTime = 0;
\r
905 int64 nRepaintInterval = 500;
\r
907 void ThreadDelayedRepaint(void* parg)
\r
911 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
913 nLastRepaint = nNeedRepaint;
\r
916 printf("DelayedRepaint\n");
\r
917 wxPaintEvent event;
\r
918 pframeMain->fRefresh = true;
\r
919 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
922 Sleep(nRepaintInterval);
\r
926 void MainFrameRepaint()
\r
928 // This is called by network code that shouldn't access pframeMain
\r
929 // directly because it could still be running after the UI is closed.
\r
932 // Don't repaint too often
\r
933 static int64 nLastRepaintRequest;
\r
934 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
939 nLastRepaintRequest = GetTimeMillis();
\r
941 printf("MainFrameRepaint\n");
\r
942 wxPaintEvent event;
\r
943 pframeMain->fRefresh = true;
\r
944 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
948 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
950 // Skip lets the listctrl do the paint, we're just hooking the message
\r
954 ptaskbaricon->UpdateTooltip();
\r
959 static int nTransactionCount;
\r
960 bool fPaintedBalance = false;
\r
961 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
963 nLastRepaint = nNeedRepaint;
\r
964 nLastRepaintTime = GetTimeMillis();
\r
966 // Update listctrl contents
\r
967 if (!vWalletUpdated.empty())
\r
969 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
972 if (m_listCtrl->GetItemCount())
\r
973 strTop = (string)m_listCtrl->GetItemText(0);
\r
974 foreach(uint256 hash, vWalletUpdated)
\r
976 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
977 if (mi != mapWallet.end())
\r
978 InsertTransaction((*mi).second, false);
\r
980 vWalletUpdated.clear();
\r
981 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
982 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
987 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
989 fPaintedBalance = true;
\r
990 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
992 // Count hidden and multi-line transactions
\r
993 nTransactionCount = 0;
\r
994 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
996 CWalletTx& wtx = (*it).second;
\r
997 nTransactionCount += wtx.nLinesDisplayed;
\r
1001 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
1004 // Update status column of visible items only
\r
1005 RefreshStatusColumn();
\r
1007 // Update status bar
\r
1008 string strGen = "";
\r
1009 if (fGenerateBitcoins)
\r
1010 strGen = _(" Generating");
\r
1011 if (fGenerateBitcoins && vNodes.empty())
\r
1012 strGen = _("(not connected)");
\r
1013 m_statusBar->SetStatusText(strGen, 1);
\r
1015 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
1016 m_statusBar->SetStatusText(strStatus, 2);
\r
1018 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
1019 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
1021 // Update receiving address
\r
1022 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
\r
1023 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
\r
1024 m_textCtrlAddress->SetValue(strDefaultAddress);
\r
1028 void UIThreadCall(boost::function0<void> fn)
\r
1030 // Call this with a function object created with bind.
\r
1031 // bind needs all parameters to match the function's expected types
\r
1032 // and all default parameters specified. Some examples:
\r
1033 // UIThreadCall(bind(wxBell));
\r
1034 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
1035 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
1038 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
1039 event.SetClientData((void*)new boost::function0<void>(fn));
\r
1040 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
1044 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
1046 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1051 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1057 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1059 // Options->Generate Coins
\r
1060 GenerateBitcoins(event.IsChecked());
\r
1063 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1065 event.Check(fGenerateBitcoins);
\r
1068 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1070 // Options->Your Receiving Addresses
\r
1071 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
\r
1072 if (!dialog.ShowModal())
\r
1076 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1078 // Options->Options
\r
1079 COptionsDialog dialog(this);
\r
1080 dialog.ShowModal();
\r
1083 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1086 CAboutDialog dialog(this);
\r
1087 dialog.ShowModal();
\r
1090 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1093 CSendDialog dialog(this);
\r
1094 dialog.ShowModal();
\r
1097 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1099 // Toolbar: Address Book
\r
1100 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
\r
1101 if (dialogAddr.ShowModal() == 2)
\r
1104 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
\r
1105 dialogSend.ShowModal();
\r
1109 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1111 // Automatically select-all when entering window
\r
1113 m_textCtrlAddress->SetSelection(-1, -1);
\r
1114 fOnSetFocusAddress = true;
\r
1117 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1120 if (fOnSetFocusAddress)
\r
1121 m_textCtrlAddress->SetSelection(-1, -1);
\r
1122 fOnSetFocusAddress = false;
\r
1125 void CMainFrame::OnButtonNew(wxCommandEvent& event)
\r
1128 CGetTextFromUserDialog dialog(this,
\r
1129 _("New Receiving Address"),
\r
1130 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
\r
1132 if (!dialog.ShowModal())
\r
1134 string strName = dialog.GetValue();
\r
1136 // Generate new key
\r
1137 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
1140 SetAddressBookName(strAddress, strName);
\r
1141 SetDefaultReceivingAddress(strAddress);
\r
1144 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1146 // Copy address box to clipboard
\r
1147 if (wxTheClipboard->Open())
\r
1149 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1150 wxTheClipboard->Close();
\r
1154 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1156 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1158 CRITICAL_BLOCK(cs_mapWallet)
\r
1160 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1161 if (mi == mapWallet.end())
\r
1163 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1166 wtx = (*mi).second;
\r
1168 CTxDetailsDialog dialog(this, wtx);
\r
1169 dialog.ShowModal();
\r
1170 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1171 //pdialog->Show();
\r
1179 //////////////////////////////////////////////////////////////////////////////
\r
1181 // CTxDetailsDialog
\r
1184 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1186 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1189 strHTML.reserve(4000);
\r
1190 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1192 int64 nTime = wtx.GetTxTime();
\r
1193 int64 nCredit = wtx.GetCredit();
\r
1194 int64 nDebit = wtx.GetDebit();
\r
1195 int64 nNet = nCredit - nDebit;
\r
1199 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
\r
1200 int nRequests = wtx.GetRequestCount();
\r
1201 if (nRequests != -1)
\r
1203 if (nRequests == 0)
\r
1204 strHTML += _(", has not been successfully broadcast yet");
\r
1205 else if (nRequests == 1)
\r
1206 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
\r
1208 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
\r
1210 strHTML += "<br>";
\r
1212 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1218 if (wtx.IsCoinBase())
\r
1220 strHTML += _("<b>Source:</b> Generated<br>");
\r
1222 else if (!wtx.mapValue["from"].empty())
\r
1224 // Online transaction
\r
1225 if (!wtx.mapValue["from"].empty())
\r
1226 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1230 // Offline transaction
\r
1234 foreach(const CTxOut& txout, wtx.vout)
\r
1236 if (txout.IsMine())
\r
1238 vector<unsigned char> vchPubKey;
\r
1239 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1241 string strAddress = PubKeyToAddress(vchPubKey);
\r
1242 if (mapAddressBook.count(strAddress))
\r
1244 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
\r
1245 strHTML += _("<b>To:</b> ");
\r
1246 strHTML += HtmlEscape(strAddress);
\r
1247 if (!mapAddressBook[strAddress].empty())
\r
1248 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
\r
1250 strHTML += _(" (yours)");
\r
1251 strHTML += "<br>";
\r
1264 string strAddress;
\r
1265 if (!wtx.mapValue["to"].empty())
\r
1267 // Online transaction
\r
1268 strAddress = wtx.mapValue["to"];
\r
1269 strHTML += _("<b>To:</b> ");
\r
1270 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1271 strHTML += mapAddressBook[strAddress] + " ";
\r
1272 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1279 if (wtx.IsCoinBase() && nCredit == 0)
\r
1284 int64 nUnmatured = 0;
\r
1285 foreach(const CTxOut& txout, wtx.vout)
\r
1286 nUnmatured += txout.GetCredit();
\r
1287 strHTML += _("<b>Credit:</b> ");
\r
1288 if (wtx.IsInMainChain())
\r
1289 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1291 strHTML += _("(not accepted)");
\r
1292 strHTML += "<br>";
\r
1294 else if (nNet > 0)
\r
1299 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
\r
1303 bool fAllFromMe = true;
\r
1304 foreach(const CTxIn& txin, wtx.vin)
\r
1305 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1307 bool fAllToMe = true;
\r
1308 foreach(const CTxOut& txout, wtx.vout)
\r
1309 fAllToMe = fAllToMe && txout.IsMine();
\r
1316 foreach(const CTxOut& txout, wtx.vout)
\r
1318 if (txout.IsMine())
\r
1321 if (wtx.mapValue["to"].empty())
\r
1323 // Offline transaction
\r
1325 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1327 string strAddress = Hash160ToAddress(hash160);
\r
1328 strHTML += _("<b>To:</b> ");
\r
1329 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1330 strHTML += mapAddressBook[strAddress] + " ";
\r
1331 strHTML += strAddress;
\r
1332 strHTML += "<br>";
\r
1336 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
\r
1341 // Payment to self
\r
1342 /// issue: can't tell which is the payment and which is the change anymore
\r
1343 //int64 nValue = wtx.vout[0].nValue;
\r
1344 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
\r
1345 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
\r
1348 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1350 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
\r
1355 // Mixed debit transaction
\r
1357 foreach(const CTxIn& txin, wtx.vin)
\r
1358 if (txin.IsMine())
\r
1359 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1360 foreach(const CTxOut& txout, wtx.vout)
\r
1361 if (txout.IsMine())
\r
1362 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
\r
1366 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
\r
1372 if (!wtx.mapValue["message"].empty())
\r
1373 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1375 if (wtx.IsCoinBase())
\r
1376 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
1384 strHTML += "<hr><br>debug print<br><br>";
\r
1385 foreach(const CTxIn& txin, wtx.vin)
\r
1386 if (txin.IsMine())
\r
1387 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1388 foreach(const CTxOut& txout, wtx.vout)
\r
1389 if (txout.IsMine())
\r
1390 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1392 strHTML += "<b>Inputs:</b><br>";
\r
1393 CRITICAL_BLOCK(cs_mapWallet)
\r
1395 foreach(const CTxIn& txin, wtx.vin)
\r
1397 COutPoint prevout = txin.prevout;
\r
1398 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1399 if (mi != mapWallet.end())
\r
1401 const CWalletTx& prev = (*mi).second;
\r
1402 if (prevout.n < prev.vout.size())
\r
1404 strHTML += HtmlEscape(prev.ToString(), true);
\r
1405 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1406 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1412 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1413 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1418 strHTML += "</font></html>";
\r
1419 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1420 m_htmlWin->SetPage(strHTML);
\r
1421 m_buttonOK->SetFocus();
\r
1425 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1435 //////////////////////////////////////////////////////////////////////////////
\r
1440 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1442 // Set up list box of page choices
\r
1443 m_listBox->Append(_("Main"));
\r
1444 //m_listBox->Append(_("Test 2"));
\r
1445 m_listBox->SetSelection(0);
\r
1448 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
\r
1449 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1453 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1454 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1455 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1456 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1457 int nProcessors = wxThread::GetCPUCount();
\r
1458 if (nProcessors < 1)
\r
1459 nProcessors = 999;
\r
1460 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1461 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1462 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1463 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1464 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1465 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1466 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1467 m_staticTextProxyIP->Enable(fUseProxy);
\r
1468 m_staticTextProxyPort->Enable(fUseProxy);
\r
1469 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1470 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1472 m_buttonOK->SetFocus();
\r
1475 void COptionsDialog::SelectPage(int nPage)
\r
1477 m_panelMain->Show(nPage == 0);
\r
1478 m_panelTest2->Show(nPage == 1);
\r
1480 m_scrolledWindow->Layout();
\r
1481 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1484 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1486 SelectPage(event.GetSelection());
\r
1489 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1492 int64 nTmp = nTransactionFee;
\r
1493 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1494 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1497 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1499 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1502 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1504 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1505 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1506 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1507 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1510 CAddress COptionsDialog::GetProxyAddr()
\r
1512 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1513 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1514 if (addr.ip == INADDR_NONE)
\r
1515 addr.ip = addrProxy.ip;
\r
1516 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1517 addr.port = htons(nPort);
\r
1518 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1519 addr.port = addrProxy.port;
\r
1523 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1526 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1527 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1531 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1533 OnButtonApply(event);
\r
1537 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1542 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1544 CWalletDB walletdb;
\r
1546 int64 nPrevTransactionFee = nTransactionFee;
\r
1547 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1548 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1550 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1551 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1553 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1554 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1556 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1558 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1559 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1561 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1562 GenerateBitcoins(fGenerateBitcoins);
\r
1564 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1566 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1567 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1570 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1572 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1573 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1574 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1577 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1579 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1580 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1583 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1584 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1586 addrProxy = GetProxyAddr();
\r
1587 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1594 //////////////////////////////////////////////////////////////////////////////
\r
1599 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1601 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d"), VERSION/10000, (VERSION/100)%100, VERSION%100));
\r
1603 // Change (c) into UTF-8 or ANSI copyright symbol
\r
1604 wxString str = m_staticTextMain->GetLabel();
\r
1606 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
\r
1608 str.Replace("(c)", "\xA9");
\r
1610 m_staticTextMain->SetLabel(str);
\r
1612 // Resize on Linux to make the window fit the text.
\r
1613 // The text was wrapped manually rather than using the Wrap setting because
\r
1614 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1615 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1616 if (fontTmp.GetPointSize() > 8);
\r
1617 fontTmp.SetPointSize(8);
\r
1618 m_staticTextMain->SetFont(fontTmp);
\r
1619 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
\r
1623 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1633 //////////////////////////////////////////////////////////////////////////////
\r
1638 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1641 m_textCtrlAddress->SetValue(strAddress);
\r
1642 m_choiceTransferType->SetSelection(0);
\r
1643 m_bitmapCheckMark->Show(false);
\r
1644 fEnabledPrev = true;
\r
1645 m_textCtrlAddress->SetFocus();
\r
1646 //// todo: should add a display of your balance for convenience
\r
1648 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1649 if (fontTmp.GetPointSize() > 9);
\r
1650 fontTmp.SetPointSize(9);
\r
1651 m_staticTextInstructions->SetFont(fontTmp);
\r
1652 SetSize(725, 380);
\r
1657 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1658 SetIcon(iconSend);
\r
1660 wxCommandEvent event;
\r
1661 OnTextAddress(event);
\r
1663 // Fixup the tab order
\r
1664 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1665 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1669 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1673 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1674 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1676 // Grey out message if bitcoin address
\r
1677 bool fEnable = !fBitcoinAddress;
\r
1678 m_staticTextFrom->Enable(fEnable);
\r
1679 m_textCtrlFrom->Enable(fEnable);
\r
1680 m_staticTextMessage->Enable(fEnable);
\r
1681 m_textCtrlMessage->Enable(fEnable);
\r
1682 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1683 if (!fEnable && fEnabledPrev)
\r
1685 strFromSave = m_textCtrlFrom->GetValue();
\r
1686 strMessageSave = m_textCtrlMessage->GetValue();
\r
1687 m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
\r
1688 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
\r
1690 else if (fEnable && !fEnabledPrev)
\r
1692 m_textCtrlFrom->SetValue(strFromSave);
\r
1693 m_textCtrlMessage->SetValue(strMessageSave);
\r
1695 fEnabledPrev = fEnable;
\r
1698 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1700 // Reformat the amount
\r
1702 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1705 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1706 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1709 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1711 // Open address book
\r
1712 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
\r
1713 if (dialog.ShowModal())
\r
1714 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
\r
1717 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1719 // Copy clipboard to address box
\r
1720 if (wxTheClipboard->Open())
\r
1722 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1724 wxTextDataObject data;
\r
1725 wxTheClipboard->GetData(data);
\r
1726 m_textCtrlAddress->SetValue(data.GetText());
\r
1728 wxTheClipboard->Close();
\r
1732 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1735 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1739 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1741 wxMessageBox(_("Error in amount "), _("Send Coins"));
\r
1744 if (nValue > GetBalance())
\r
1746 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
\r
1749 if (nValue + nTransactionFee > GetBalance())
\r
1751 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
\r
1755 // Parse bitcoin address
\r
1757 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1759 if (fBitcoinAddress)
\r
1761 // Send to bitcoin address
\r
1762 CScript scriptPubKey;
\r
1763 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1765 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
\r
1766 if (strError == "")
\r
1767 wxMessageBox(_("Payment sent "), _("Sending..."));
\r
1768 else if (strError != "ABORTED")
\r
1769 wxMessageBox(strError + " ", _("Sending..."));
\r
1773 // Parse IP address
\r
1774 CAddress addr(strAddress);
\r
1775 if (!addr.IsValid())
\r
1777 wxMessageBox(_("Invalid address "), _("Send Coins"));
\r
1782 wtx.mapValue["to"] = strAddress;
\r
1783 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1784 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1786 // Send to IP address
\r
1787 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1788 if (!pdialog->ShowModal())
\r
1792 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1793 if (!mapAddressBook.count(strAddress))
\r
1794 SetAddressBookName(strAddress, "");
\r
1799 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1810 //////////////////////////////////////////////////////////////////////////////
\r
1815 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
1818 nPrice = nPriceIn;
\r
1820 start = wxDateTime::UNow();
\r
1821 memset(pszStatus, 0, sizeof(pszStatus));
\r
1822 fCanCancel = true;
\r
1826 fWorkDone = false;
\r
1828 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
\r
1831 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1832 m_textCtrlStatus->SetValue("");
\r
1834 CreateThread(SendingDialogStartTransfer, this);
\r
1837 CSendingDialog::~CSendingDialog()
\r
1839 printf("~CSendingDialog()\n");
\r
1842 void CSendingDialog::Close()
\r
1844 // Last one out turn out the lights.
\r
1845 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1846 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1847 // This allows the window to disappear and end modality when cancelled
\r
1848 // without making the user wait for ConnectNode to return. The dialog object
\r
1849 // hangs around in the background until the work thread exits.
\r
1851 EndModal(fSuccess);
\r
1860 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1862 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1869 wxCommandEvent cmdevent;
\r
1870 OnButtonCancel(cmdevent);
\r
1874 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1880 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1886 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1889 if (strlen(pszStatus) > 130)
\r
1890 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1892 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1893 m_staticTextSending->SetFocus();
\r
1895 m_buttonCancel->Enable(false);
\r
1898 m_buttonOK->Enable(true);
\r
1899 m_buttonOK->SetFocus();
\r
1900 m_buttonCancel->Enable(false);
\r
1902 if (fAbort && fCanCancel && IsShown())
\r
1904 strcpy(pszStatus, _("CANCELLED"));
\r
1905 m_buttonOK->Enable(true);
\r
1906 m_buttonOK->SetFocus();
\r
1907 m_buttonCancel->Enable(false);
\r
1908 m_buttonCancel->SetLabel(_("Cancelled"));
\r
1910 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
\r
1916 // Everything from here on is not in the UI thread and must only communicate
\r
1917 // with the rest of the dialog through variables and calling repaint.
\r
1920 void CSendingDialog::Repaint()
\r
1923 wxPaintEvent event;
\r
1924 GetEventHandler()->AddPendingEvent(event);
\r
1927 bool CSendingDialog::Status()
\r
1934 if (fAbort && fCanCancel)
\r
1936 memset(pszStatus, 0, 10);
\r
1937 strcpy(pszStatus, _("CANCELLED"));
\r
1945 bool CSendingDialog::Status(const string& str)
\r
1950 // This can be read by the UI thread at any time,
\r
1951 // so copy in a way that can be read cleanly at all times.
\r
1952 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
1953 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
1959 bool CSendingDialog::Error(const string& str)
\r
1961 fCanCancel = false;
\r
1963 Status(string(_("Error: ")) + str);
\r
1967 void SendingDialogStartTransfer(void* parg)
\r
1969 ((CSendingDialog*)parg)->StartTransfer();
\r
1972 void CSendingDialog::StartTransfer()
\r
1974 // Make sure we have enough money
\r
1975 if (nPrice + nTransactionFee > GetBalance())
\r
1977 Error(_("Insufficient funds"));
\r
1981 // We may have connected already for product details
\r
1982 if (!Status(_("Connecting...")))
\r
1984 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
1987 Error(_("Unable to connect"));
\r
1991 // Send order to seller, with response going to OnReply2 via event handler
\r
1992 if (!Status(_("Requesting public key...")))
\r
1994 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
1997 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
1999 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
2002 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
2004 if (!Status(_("Received public key...")))
\r
2007 CScript scriptPubKey;
\r
2014 string strMessage;
\r
2015 vRecv >> strMessage;
\r
2016 Error(_("Transfer was not accepted"));
\r
2017 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
2020 vRecv >> scriptPubKey;
\r
2024 //// what do we want to do about this?
\r
2025 Error(_("Invalid response received"));
\r
2029 // Pause to give the user a chance to cancel
\r
2030 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
2037 CRITICAL_BLOCK(cs_main)
\r
2040 if (!Status(_("Creating transaction...")))
\r
2042 if (nPrice + nTransactionFee > GetBalance())
\r
2044 Error(_("Insufficient funds"));
\r
2048 int64 nFeeRequired;
\r
2049 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2051 if (nPrice + nFeeRequired > GetBalance())
\r
2052 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
\r
2054 Error(_("Transaction creation failed"));
\r
2058 // Transaction fee
\r
2059 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
\r
2061 Error(_("Transaction aborted"));
\r
2065 // Make sure we're still connected
\r
2066 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2069 Error(_("Lost connection, transaction cancelled"));
\r
2073 // Last chance to cancel
\r
2077 fCanCancel = false;
\r
2080 fCanCancel = true;
\r
2083 fCanCancel = false;
\r
2085 if (!Status(_("Sending payment...")))
\r
2089 if (!CommitTransaction(wtx, key))
\r
2091 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
2095 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2096 CWalletTx wtxSend = wtx;
\r
2097 wtxSend.fFromMe = false;
\r
2098 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
\r
2100 Status(_("Waiting for confirmation..."));
\r
2101 MainFrameRepaint();
\r
2105 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2107 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2110 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2118 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
\r
2119 "The transaction is recorded and will credit to the recipient,\n"
\r
2120 "but the comment information will be blank."));
\r
2126 //// what do we want to do about this?
\r
2127 Error(_("Payment was sent, but an invalid response was received"));
\r
2133 Status(_("Payment completed"));
\r
2141 //////////////////////////////////////////////////////////////////////////////
\r
2143 // CAddressBookDialog
\r
2146 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
\r
2148 // Set initially selected page
\r
2149 wxNotebookEvent event;
\r
2150 event.SetSelection(nPageIn);
\r
2151 OnNotebookPageChanged(event);
\r
2152 m_notebook->ChangeSelection(nPageIn);
\r
2154 fDuringSend = fDuringSendIn;
\r
2156 m_buttonCancel->Show(false);
\r
2159 wxIcon iconAddressBook;
\r
2160 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2161 SetIcon(iconAddressBook);
\r
2163 // Init column headers
\r
2164 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
\r
2165 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
\r
2166 m_listCtrlSending->SetFocus();
\r
2167 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
\r
2168 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
\r
2169 m_listCtrlReceiving->SetFocus();
\r
2171 // Fill listctrl with address book data
\r
2172 CRITICAL_BLOCK(cs_mapKeys)
\r
2173 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2175 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
\r
2176 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2178 string strAddress = item.first;
\r
2179 string strName = item.second;
\r
2181 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2182 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
\r
2183 int nIndex = InsertLine(plistCtrl, strName, strAddress);
\r
2184 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
\r
2185 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2190 wxString CAddressBookDialog::GetSelectedAddress()
\r
2192 int nIndex = GetSelection(m_listCtrl);
\r
2195 return GetItemText(m_listCtrl, nIndex, 1);
\r
2198 wxString CAddressBookDialog::GetSelectedSendingAddress()
\r
2200 int nIndex = GetSelection(m_listCtrlSending);
\r
2203 return GetItemText(m_listCtrlSending, nIndex, 1);
\r
2206 wxString CAddressBookDialog::GetSelectedReceivingAddress()
\r
2208 int nIndex = GetSelection(m_listCtrlReceiving);
\r
2211 return GetItemText(m_listCtrlReceiving, nIndex, 1);
\r
2214 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
\r
2217 nPage = event.GetSelection();
\r
2218 if (nPage == SENDING)
\r
2219 m_listCtrl = m_listCtrlSending;
\r
2220 else if (nPage == RECEIVING)
\r
2221 m_listCtrl = m_listCtrlReceiving;
\r
2222 m_buttonDelete->Show(nPage == SENDING);
\r
2223 m_buttonCopy->Show(nPage == RECEIVING);
\r
2225 m_listCtrl->SetFocus();
\r
2228 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2230 // Update address book with edited name
\r
2232 if (event.IsEditCancelled())
\r
2234 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2235 SetAddressBookName(strAddress, string(event.GetText()));
\r
2236 pframeMain->RefreshListCtrl();
\r
2239 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2242 if (nPage == RECEIVING)
\r
2243 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
\r
2246 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2251 // Doubleclick returns selection
\r
2252 EndModal(GetSelectedAddress() != "" ? 2 : 0);
\r
2256 // Doubleclick edits item
\r
2257 wxCommandEvent event2;
\r
2258 OnButtonEdit(event2);
\r
2261 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2263 if (nPage != SENDING)
\r
2265 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2267 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2269 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2270 CWalletDB().EraseName(strAddress);
\r
2271 m_listCtrl->DeleteItem(nIndex);
\r
2274 pframeMain->RefreshListCtrl();
\r
2277 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2279 // Copy address box to clipboard
\r
2280 if (wxTheClipboard->Open())
\r
2282 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
\r
2283 wxTheClipboard->Close();
\r
2287 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2290 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2292 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
\r
2296 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2298 int nIndex = GetSelection(m_listCtrl);
\r
2301 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2302 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2303 string strAddressOrg = strAddress;
\r
2305 if (nPage == SENDING)
\r
2307 // Ask name and address
\r
2310 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
\r
2311 if (!dialog.ShowModal())
\r
2313 strName = dialog.GetValue1();
\r
2314 strAddress = dialog.GetValue2();
\r
2316 while (CheckIfMine(strAddress, _("Edit Address")));
\r
2319 else if (nPage == RECEIVING)
\r
2322 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
\r
2323 if (!dialog.ShowModal())
\r
2325 strName = dialog.GetValue();
\r
2329 if (strAddress != strAddressOrg)
\r
2330 CWalletDB().EraseName(strAddressOrg);
\r
2331 SetAddressBookName(strAddress, strName);
\r
2332 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2333 m_listCtrl->SetItemText(nIndex, strName);
\r
2334 pframeMain->RefreshListCtrl();
\r
2337 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2340 string strAddress;
\r
2342 if (nPage == SENDING)
\r
2344 // Ask name and address
\r
2347 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
\r
2348 if (!dialog.ShowModal())
\r
2350 strName = dialog.GetValue1();
\r
2351 strAddress = dialog.GetValue2();
\r
2353 while (CheckIfMine(strAddress, _("Add Address")));
\r
2355 else if (nPage == RECEIVING)
\r
2358 CGetTextFromUserDialog dialog(this,
\r
2359 _("New Receiving Address"),
\r
2360 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
\r
2362 if (!dialog.ShowModal())
\r
2364 strName = dialog.GetValue();
\r
2366 // Generate new key
\r
2367 strAddress = PubKeyToAddress(GenerateNewKey());
\r
2370 // Add to list and select it
\r
2371 SetAddressBookName(strAddress, strName);
\r
2372 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2373 SetSelection(m_listCtrl, nIndex);
\r
2374 m_listCtrl->SetFocus();
\r
2375 if (nPage == SENDING)
\r
2376 pframeMain->RefreshListCtrl();
\r
2379 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2382 EndModal(GetSelectedAddress() != "" ? 1 : 0);
\r
2385 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2391 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2402 //////////////////////////////////////////////////////////////////////////////
\r
2409 ID_TASKBAR_RESTORE = 10001,
\r
2410 ID_TASKBAR_OPTIONS,
\r
2411 ID_TASKBAR_GENERATE,
\r
2415 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
2416 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
2417 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
2418 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
2419 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
2420 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
2421 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
2424 void CMyTaskBarIcon::Show(bool fShow)
\r
2426 static char pszPrevTip[200];
\r
2429 string strTooltip = _("Bitcoin");
\r
2430 if (fGenerateBitcoins)
\r
2431 strTooltip = _("Bitcoin - Generating");
\r
2432 if (fGenerateBitcoins && vNodes.empty())
\r
2433 strTooltip = _("Bitcoin - (not connected)");
\r
2435 // Optimization, only update when changed, using char array to be reentrant
\r
2436 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
2438 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
2440 // somehow it'll choose the wrong size and scale it down if
\r
2441 // we use the main icon, so we hand it one with only 16x16
\r
2442 SetIcon(wxICON(favicon), strTooltip);
\r
2444 SetIcon(bitcoin80_xpm, strTooltip);
\r
2450 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
2455 void CMyTaskBarIcon::Hide()
\r
2460 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
2465 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
2470 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
2472 // Since it's modal, get the main window to do it
\r
2473 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
\r
2474 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
2477 void CMyTaskBarIcon::Restore()
\r
2479 pframeMain->Show();
\r
2480 wxIconizeEvent event(0, false);
\r
2481 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
2482 pframeMain->Iconize(false);
\r
2483 pframeMain->Raise();
\r
2486 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
2488 GenerateBitcoins(event.IsChecked());
\r
2491 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
2493 event.Check(fGenerateBitcoins);
\r
2496 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
2498 pframeMain->Close(true);
\r
2501 void CMyTaskBarIcon::UpdateTooltip()
\r
2503 if (IsIconInstalled())
\r
2507 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
2509 wxMenu* pmenu = new wxMenu;
\r
2510 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
\r
2511 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
\r
2512 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
\r
2513 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
2514 pmenu->AppendSeparator();
\r
2515 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
\r
2532 void CreateMainWindow()
\r
2534 pframeMain = new CMainFrame(NULL);
\r
2535 if (mapArgs.count("-min"))
\r
2536 pframeMain->Iconize(true);
\r
2537 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
2538 if (fMinimizeToTray && pframeMain->IsIconized())
\r
2539 fClosedToTray = true;
\r
2540 pframeMain->Show(!fClosedToTray);
\r
2541 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
2542 CreateThread(ThreadDelayedRepaint, NULL);
\r