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
10 bool GetStartOnSystemStartup();
\r
11 void SetStartOnSystemStartup(bool fAutoStart);
\r
15 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
\r
17 CMainFrame* pframeMain = NULL;
\r
18 CMyTaskBarIcon* ptaskbaricon = NULL;
\r
19 extern int g_isPainting;
\r
20 bool fClosedToTray = false;
\r
23 int fShowGenerated = true;
\r
24 int fMinimizeToTray = true;
\r
25 int fMinimizeOnClose = true;
\r
33 //////////////////////////////////////////////////////////////////////////////
\r
38 void HandleCtrlA(wxKeyEvent& event)
\r
40 // Ctrl-a select all
\r
41 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
\r
42 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
\r
43 textCtrl->SetSelection(-1, -1);
\r
49 //char pszHourFormat[256];
\r
50 //pszHourFormat[0] = '\0';
\r
51 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
\r
52 //return (pszHourFormat[0] != '0');
\r
56 string DateStr(int64 nTime)
\r
58 // Can only be used safely here in the UI
\r
59 return (string)wxDateTime((time_t)nTime).FormatDate();
\r
62 string DateTimeStr(int64 nTime)
\r
64 // Can only be used safely here in the UI
\r
65 wxDateTime datetime((time_t)nTime);
\r
67 return (string)datetime.Format("%x %H:%M");
\r
69 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
\r
72 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
\r
74 // Helper to simplify access to listctrl
\r
76 item.m_itemId = nIndex;
\r
77 item.m_col = nColumn;
\r
78 item.m_mask = wxLIST_MASK_TEXT;
\r
79 if (!listCtrl->GetItem(item))
\r
81 return item.GetText();
\r
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
\r
86 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
87 listCtrl->SetItem(nIndex, 1, str1);
\r
91 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
93 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
94 listCtrl->SetItem(nIndex, 1, str1);
\r
95 listCtrl->SetItem(nIndex, 2, str2);
\r
96 listCtrl->SetItem(nIndex, 3, str3);
\r
97 listCtrl->SetItem(nIndex, 4, str4);
\r
101 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
\r
103 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
\r
104 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
\r
105 listCtrl->SetItem(nIndex, 1, str1);
\r
106 listCtrl->SetItem(nIndex, 2, str2);
\r
107 listCtrl->SetItem(nIndex, 3, str3);
\r
108 listCtrl->SetItem(nIndex, 4, str4);
\r
112 void SetSelection(wxListCtrl* listCtrl, int nIndex)
\r
114 int nSize = listCtrl->GetItemCount();
\r
115 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
116 for (int i = 0; i < nSize; i++)
\r
117 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
\r
120 int GetSelection(wxListCtrl* listCtrl)
\r
122 int nSize = listCtrl->GetItemCount();
\r
123 for (int i = 0; i < nSize; i++)
\r
124 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
\r
129 string HtmlEscape(const char* psz, bool fMultiLine=false)
\r
132 for (const char* p = psz; *p; p++)
\r
134 if (*p == '<') len += 4;
\r
135 else if (*p == '>') len += 4;
\r
136 else if (*p == '&') len += 5;
\r
137 else if (*p == '"') len += 6;
\r
138 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
\r
139 else if (*p == '\n' && fMultiLine) len += 5;
\r
145 for (const char* p = psz; *p; p++)
\r
147 if (*p == '<') str += "<";
\r
148 else if (*p == '>') str += ">";
\r
149 else if (*p == '&') str += "&";
\r
150 else if (*p == '"') str += """;
\r
151 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
\r
152 else if (*p == '\n' && fMultiLine) str += "<br>\n";
\r
159 string HtmlEscape(const string& str, bool fMultiLine=false)
\r
161 return HtmlEscape(str.c_str(), fMultiLine);
\r
164 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
\r
166 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
\r
170 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
\r
174 printf("wxMessageBox %s: %s\n", caption.c_str(), message.c_str());
\r
179 return wxMessageBox(message, caption, style, parent, x, y);
\r
181 if (wxThread::IsMain())
\r
183 return wxMessageBox(message, caption, style, parent, x, y);
\r
188 bool fDone = false;
\r
189 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
\r
206 //////////////////////////////////////////////////////////////////////////////
\r
211 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
\r
213 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
\r
216 fRefreshListCtrl = false;
\r
217 fRefreshListCtrlRunning = false;
\r
218 fOnSetFocusAddress = false;
\r
220 m_choiceFilter->SetSelection(0);
\r
221 double dResize = 1.0;
\r
223 SetIcon(wxICON(bitcoin));
\r
225 SetIcon(bitcoin16_xpm);
\r
226 wxFont fontTmp = m_staticText41->GetFont();
\r
227 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
\r
228 m_staticTextBalance->SetFont(fontTmp);
\r
229 m_staticTextBalance->SetSize(140, 17);
\r
230 // & underlines don't work on the toolbar buttons on gtk
\r
231 m_toolBar->ClearTools();
\r
232 m_toolBar->AddTool(wxID_BUTTONSEND, "Send Coins", wxBitmap(send20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
233 m_toolBar->AddTool(wxID_BUTTONRECEIVE, "Address Book", wxBitmap(addressbook20_xpm), wxNullBitmap, wxITEM_NORMAL, wxEmptyString, wxEmptyString);
\r
234 m_toolBar->Realize();
\r
235 // resize to fit ubuntu's huge default font
\r
237 SetSize((dResize + 0.02) * GetSize().GetWidth(), 1.09 * GetSize().GetHeight());
\r
239 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
240 m_listCtrl->SetFocus();
\r
241 ptaskbaricon = new CMyTaskBarIcon();
\r
243 // Init column headers
\r
244 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
\r
245 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
\r
247 m_listCtrl->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
248 m_listCtrl->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
\r
249 m_listCtrl->InsertColumn(2, "Status", wxLIST_FORMAT_LEFT, dResize * 110);
\r
250 m_listCtrl->InsertColumn(3, "Date", wxLIST_FORMAT_LEFT, dResize * nDateWidth);
\r
251 m_listCtrl->InsertColumn(4, "Description", wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
\r
252 m_listCtrl->InsertColumn(5, "Debit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
253 m_listCtrl->InsertColumn(6, "Credit", wxLIST_FORMAT_RIGHT, dResize * 79);
\r
256 int pnWidths[3] = { -100, 88, 290 };
\r
258 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
\r
259 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
\r
261 m_statusBar->SetFieldsCount(3, pnWidths);
\r
263 // Fill your address text box
\r
264 vector<unsigned char> vchPubKey;
\r
265 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
\r
266 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
\r
268 // Fill listctrl with wallet transactions
\r
272 CMainFrame::~CMainFrame()
\r
275 delete ptaskbaricon;
\r
276 ptaskbaricon = NULL;
\r
279 void ExitTimeout(void* parg)
\r
287 void Shutdown(void* parg)
\r
289 static CCriticalSection cs_Shutdown;
\r
290 static bool fTaken;
\r
292 CRITICAL_BLOCK(cs_Shutdown)
\r
294 fFirstThread = !fTaken;
\r
301 nTransactionsUpdated++;
\r
305 CreateThread(ExitTimeout, NULL);
\r
307 printf("Bitcoin exiting\n\n");
\r
320 void CMainFrame::OnClose(wxCloseEvent& event)
\r
322 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
\r
324 // Divert close to minimize
\r
326 fClosedToTray = true;
\r
332 CreateThread(Shutdown, NULL);
\r
336 void CMainFrame::OnIconize(wxIconizeEvent& event)
\r
338 // Hide the task bar button when minimized.
\r
339 // Event is sent when the frame is minimized or restored.
\r
340 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
\r
341 // to get rid of the deprecated warning. Just ignore it.
\r
342 if (!event.Iconized())
\r
343 fClosedToTray = false;
\r
345 // Tray is not reliable on Linux gnome
\r
346 fClosedToTray = false;
\r
348 if (fMinimizeToTray && event.Iconized())
\r
349 fClosedToTray = true;
\r
350 Show(!fClosedToTray);
\r
351 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
354 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
\r
357 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
\r
358 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
\r
361 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
\r
363 // Hidden columns not resizeable
\r
364 if (event.GetColumn() <= 1 && !fDebug)
\r
368 int CMainFrame::GetSortIndex(const string& strSort)
\r
373 // The wx generic listctrl implementation used on GTK doesn't sort,
\r
374 // so we have to do it ourselves. Remember, we sort in reverse order.
\r
375 // In the wx generic implementation, they store the list of items
\r
376 // in a vector, so indexed lookups are fast, but inserts are slower
\r
377 // the closer they are to the top.
\r
379 int high = m_listCtrl->GetItemCount();
\r
382 int mid = low + ((high - low) / 2);
\r
383 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
\r
392 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
394 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
\r
395 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
\r
398 if (!fNew && nIndex == -1)
\r
400 string strHash = " " + hashKey.ToString();
\r
401 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
402 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
406 // fNew is for blind insert, only use if you're sure it's new
\r
407 if (fNew || nIndex == -1)
\r
409 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
413 // If sort key changed, must delete and reinsert to make it relocate
\r
414 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
\r
416 m_listCtrl->DeleteItem(nIndex);
\r
417 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
\r
421 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
\r
422 m_listCtrl->SetItem(nIndex, 2, str2);
\r
423 m_listCtrl->SetItem(nIndex, 3, str3);
\r
424 m_listCtrl->SetItem(nIndex, 4, str4);
\r
425 m_listCtrl->SetItem(nIndex, 5, str5);
\r
426 m_listCtrl->SetItem(nIndex, 6, str6);
\r
427 m_listCtrl->SetItemData(nIndex, nData);
\r
430 bool CMainFrame::DeleteLine(uint256 hashKey)
\r
432 long nData = *(long*)&hashKey;
\r
436 string strHash = " " + hashKey.ToString();
\r
437 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
\r
438 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
\r
442 m_listCtrl->DeleteItem(nIndex);
\r
444 return nIndex != -1;
\r
447 string FormatTxStatus(const CWalletTx& wtx)
\r
450 if (!wtx.IsFinal())
\r
452 if (wtx.nLockTime < 500000000)
\r
453 return strprintf("Open for %d blocks", nBestHeight - wtx.nLockTime);
\r
455 return strprintf("Open until %s", DateTimeStr(wtx.nLockTime).c_str());
\r
459 int nDepth = wtx.GetDepthInMainChain();
\r
460 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
461 return strprintf("%d/offline?", nDepth);
\r
462 else if (nDepth < 6)
\r
463 return strprintf("%d/unconfirmed", nDepth);
\r
465 return strprintf("%d confirmations", nDepth);
\r
469 string SingleLine(const string& strIn)
\r
472 bool fOneSpace = false;
\r
473 foreach(int c, strIn)
\r
481 if (fOneSpace && !strOut.empty())
\r
490 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
\r
492 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
\r
493 int64 nCredit = wtx.GetCredit();
\r
494 int64 nDebit = wtx.GetDebit();
\r
495 int64 nNet = nCredit - nDebit;
\r
496 uint256 hash = wtx.GetHash();
\r
497 string strStatus = FormatTxStatus(wtx);
\r
498 map<string, string> mapValue = wtx.mapValue;
\r
499 wtx.nLinesDisplayed = 1;
\r
500 nListViewUpdated++;
\r
503 if (wtx.IsCoinBase())
\r
505 // Don't show generated coin until confirmed by at least one block after it
\r
506 // so we don't get the user's hopes up until it looks like it's probably accepted.
\r
508 // It is not an error when generated blocks are not accepted. By design,
\r
509 // some percentage of blocks, like 10% or more, will end up not accepted.
\r
510 // This is the normal mechanism by which the network copes with latency.
\r
512 // We display regular transactions right away before any confirmation
\r
513 // because they can always get into some block eventually. Generated coins
\r
514 // are special because if their block is not accepted, they are not valid.
\r
516 if (wtx.GetDepthInMainChain() < 2)
\r
518 wtx.nLinesDisplayed = 0;
\r
522 // View->Show Generated
\r
523 if (!fShowGenerated)
\r
527 // Find the block the tx is in
\r
528 CBlockIndex* pindex = NULL;
\r
529 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
\r
530 if (mi != mapBlockIndex.end())
\r
531 pindex = (*mi).second;
\r
533 // Sort order, unrecorded transactions sort to the top
\r
534 string strSort = strprintf("%010d-%01d-%010u",
\r
535 (pindex ? pindex->nHeight : INT_MAX),
\r
536 (wtx.IsCoinBase() ? 1 : 0),
\r
537 wtx.nTimeReceived);
\r
540 if (nNet > 0 || wtx.IsCoinBase())
\r
545 string strDescription;
\r
547 if (wtx.IsCoinBase())
\r
550 strDescription = "Generated";
\r
553 int64 nUnmatured = 0;
\r
554 foreach(const CTxOut& txout, wtx.vout)
\r
555 nUnmatured += txout.GetCredit();
\r
556 if (wtx.IsInMainChain())
\r
558 strDescription = strprintf("Generated (%s matures in %d more blocks)", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
560 // Check if the block was requested by anyone
\r
561 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
\r
562 strDescription = "Generated - Warning: This block was not received by any other nodes and will probably not be accepted!";
\r
566 strDescription = "Generated (not accepted)";
\r
570 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
\r
572 // Online transaction
\r
573 if (!mapValue["from"].empty())
\r
574 strDescription += "From: " + mapValue["from"];
\r
575 if (!mapValue["message"].empty())
\r
577 if (!strDescription.empty())
\r
578 strDescription += " - ";
\r
579 strDescription += mapValue["message"];
\r
584 // Offline transaction
\r
585 foreach(const CTxOut& txout, wtx.vout)
\r
587 if (txout.IsMine())
\r
589 vector<unsigned char> vchPubKey;
\r
590 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
592 CRITICAL_BLOCK(cs_mapAddressBook)
\r
594 string strAddress = PubKeyToAddress(vchPubKey);
\r
595 if (mapAddressBook.count(strAddress))
\r
597 //strDescription += "Received payment to ";
\r
598 //strDescription += "Received with address ";
\r
599 strDescription += "From: unknown, To: ";
\r
600 strDescription += strAddress;
\r
601 /// The labeling feature is just too confusing, so I hid it
\r
602 /// by putting it at the end where it runs off the screen.
\r
603 /// It can still be seen by widening the column, or in the
\r
604 /// details dialog.
\r
605 if (!mapAddressBook[strAddress].empty())
\r
606 strDescription += " (" + mapAddressBook[strAddress] + ")";
\r
615 InsertLine(fNew, nIndex, hash, strSort,
\r
617 nTime ? DateTimeStr(nTime) : "",
\r
618 SingleLine(strDescription),
\r
620 FormatMoney(nNet, true));
\r
624 bool fAllFromMe = true;
\r
625 foreach(const CTxIn& txin, wtx.vin)
\r
626 fAllFromMe = fAllFromMe && txin.IsMine();
\r
628 bool fAllToMe = true;
\r
629 foreach(const CTxOut& txout, wtx.vout)
\r
630 fAllToMe = fAllToMe && txout.IsMine();
\r
632 if (fAllFromMe && fAllToMe)
\r
635 int64 nValue = wtx.vout[0].nValue;
\r
636 InsertLine(fNew, nIndex, hash, strSort,
\r
638 nTime ? DateTimeStr(nTime) : "",
\r
639 "Payment to yourself",
\r
642 /// issue: can't tell which is the payment and which is the change anymore
\r
643 // FormatMoney(nNet - nValue, true),
\r
644 // FormatMoney(nValue, true));
\r
646 else if (fAllFromMe)
\r
651 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
652 wtx.nLinesDisplayed = 0;
\r
653 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
\r
655 const CTxOut& txout = wtx.vout[nOut];
\r
656 if (txout.IsMine())
\r
660 if (!mapValue["to"].empty())
\r
662 // Online transaction
\r
663 strAddress = mapValue["to"];
\r
667 // Offline transaction
\r
669 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
670 strAddress = Hash160ToAddress(hash160);
\r
673 string strDescription = "To: ";
\r
674 CRITICAL_BLOCK(cs_mapAddressBook)
\r
675 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
676 strDescription += mapAddressBook[strAddress] + " ";
\r
677 strDescription += strAddress;
\r
678 if (!mapValue["message"].empty())
\r
680 if (!strDescription.empty())
\r
681 strDescription += " - ";
\r
682 strDescription += mapValue["message"];
\r
685 int64 nValue = txout.nValue;
\r
686 if (nOut == 0 && nTxFee > 0)
\r
689 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
\r
691 nTime ? DateTimeStr(nTime) : "",
\r
692 SingleLine(strDescription),
\r
693 FormatMoney(-nValue, true),
\r
695 wtx.nLinesDisplayed++;
\r
701 // Mixed debit transaction, can't break down payees
\r
703 bool fAllMine = true;
\r
704 foreach(const CTxOut& txout, wtx.vout)
\r
705 fAllMine = fAllMine && txout.IsMine();
\r
706 foreach(const CTxIn& txin, wtx.vin)
\r
707 fAllMine = fAllMine && txin.IsMine();
\r
709 InsertLine(fNew, nIndex, hash, strSort,
\r
711 nTime ? DateTimeStr(nTime) : "",
\r
713 FormatMoney(nNet, true),
\r
721 void CMainFrame::RefreshListCtrl()
\r
723 fRefreshListCtrl = true;
\r
727 void CMainFrame::OnIdle(wxIdleEvent& event)
\r
729 if (fRefreshListCtrl)
\r
731 // Collect list of wallet transactions and sort newest first
\r
732 bool fEntered = false;
\r
733 vector<pair<unsigned int, uint256> > vSorted;
\r
734 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
736 printf("RefreshListCtrl starting\n");
\r
738 fRefreshListCtrl = false;
\r
739 vWalletUpdated.clear();
\r
741 // Do the newest transactions first
\r
742 vSorted.reserve(mapWallet.size());
\r
743 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
745 const CWalletTx& wtx = (*it).second;
\r
746 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
\r
747 vSorted.push_back(make_pair(nTime, (*it).first));
\r
749 m_listCtrl->DeleteAllItems();
\r
754 sort(vSorted.begin(), vSorted.end());
\r
756 // Fill list control
\r
757 for (int i = 0; i < vSorted.size();)
\r
761 bool fEntered = false;
\r
762 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
765 uint256& hash = vSorted[i++].second;
\r
766 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
767 if (mi != mapWallet.end())
\r
768 InsertTransaction((*mi).second, true);
\r
770 if (!fEntered || i == 100 || i % 500 == 0)
\r
774 printf("RefreshListCtrl done\n");
\r
776 // Update transaction total display
\r
777 MainFrameRepaint();
\r
781 // Check for time updates
\r
782 static int64 nLastTime;
\r
783 if (GetTime() > nLastTime + 30)
\r
785 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
787 nLastTime = GetTime();
\r
788 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
790 CWalletTx& wtx = (*it).second;
\r
791 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
\r
792 InsertTransaction(wtx, false);
\r
799 void CMainFrame::RefreshStatusColumn()
\r
801 static int nLastTop;
\r
802 static CBlockIndex* pindexLastBest;
\r
803 static unsigned int nLastRefreshed;
\r
805 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
\r
806 if (nTop == nLastTop && pindexLastBest == pindexBest)
\r
809 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
812 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
\r
814 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
\r
816 // If no updates, only need to do the part that moved onto the screen
\r
817 if (nStart >= nLastTop && nStart < nLastTop + 100)
\r
818 nStart = nLastTop + 100;
\r
819 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
\r
823 pindexLastBest = pindexBest;
\r
824 nLastRefreshed = nListViewUpdated;
\r
826 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
\r
828 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
\r
829 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
830 if (mi == mapWallet.end())
\r
832 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
\r
835 CWalletTx& wtx = (*mi).second;
\r
836 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
\r
838 if (!InsertTransaction(wtx, false, nIndex))
\r
839 m_listCtrl->DeleteItem(nIndex--);
\r
842 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
\r
847 void CMainFrame::OnPaint(wxPaintEvent& event)
\r
858 unsigned int nNeedRepaint = 0;
\r
859 unsigned int nLastRepaint = 0;
\r
860 int64 nLastRepaintTime = 0;
\r
861 int64 nRepaintInterval = 500;
\r
863 void ThreadDelayedRepaint(void* parg)
\r
867 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
869 nLastRepaint = nNeedRepaint;
\r
872 printf("DelayedRepaint\n");
\r
873 wxPaintEvent event;
\r
874 pframeMain->fRefresh = true;
\r
875 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
878 Sleep(nRepaintInterval);
\r
882 void MainFrameRepaint()
\r
884 // This is called by network code that shouldn't access pframeMain
\r
885 // directly because it could still be running after the UI is closed.
\r
888 // Don't repaint too often
\r
889 static int64 nLastRepaintRequest;
\r
890 if (GetTimeMillis() - nLastRepaintRequest < 100)
\r
895 nLastRepaintRequest = GetTimeMillis();
\r
897 printf("MainFrameRepaint\n");
\r
898 wxPaintEvent event;
\r
899 pframeMain->fRefresh = true;
\r
900 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
904 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
\r
907 ptaskbaricon->UpdateTooltip();
\r
912 static int nTransactionCount;
\r
913 bool fPaintedBalance = false;
\r
914 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
\r
916 nLastRepaint = nNeedRepaint;
\r
917 nLastRepaintTime = GetTimeMillis();
\r
919 // Update listctrl contents
\r
920 if (!vWalletUpdated.empty())
\r
922 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
925 if (m_listCtrl->GetItemCount())
\r
926 strTop = (string)m_listCtrl->GetItemText(0);
\r
927 foreach(uint256 hash, vWalletUpdated)
\r
929 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
930 if (mi != mapWallet.end())
\r
931 InsertTransaction((*mi).second, false);
\r
933 vWalletUpdated.clear();
\r
934 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
\r
935 m_listCtrl->ScrollList(0, INT_MIN/2);
\r
940 TRY_CRITICAL_BLOCK(cs_mapWallet)
\r
942 fPaintedBalance = true;
\r
943 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
\r
945 // Count hidden and multi-line transactions
\r
946 nTransactionCount = 0;
\r
947 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
\r
949 CWalletTx& wtx = (*it).second;
\r
950 nTransactionCount += wtx.nLinesDisplayed;
\r
954 if (!vWalletUpdated.empty() || !fPaintedBalance)
\r
957 // Update status column of visible items only
\r
958 RefreshStatusColumn();
\r
960 // Update status bar
\r
961 string strGen = "";
\r
962 if (fGenerateBitcoins)
\r
963 strGen = " Generating";
\r
964 if (fGenerateBitcoins && vNodes.empty())
\r
965 strGen = "(not connected)";
\r
966 m_statusBar->SetStatusText(strGen, 1);
\r
968 string strStatus = strprintf(" %d connections %d blocks %d transactions", vNodes.size(), nBestHeight + 1, nTransactionCount);
\r
969 m_statusBar->SetStatusText(strStatus, 2);
\r
971 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
\r
972 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
\r
974 // Pass through to listctrl to actually do the paint, we're just hooking the message
\r
975 m_listCtrl->Disconnect(wxEVT_PAINT, (wxObjectEventFunction)NULL, NULL, this);
\r
976 m_listCtrl->GetEventHandler()->ProcessEvent(event);
\r
977 m_listCtrl->Connect(wxEVT_PAINT, wxPaintEventHandler(CMainFrame::OnPaintListCtrl), NULL, this);
\r
981 void UIThreadCall(boost::function0<void> fn)
\r
983 // Call this with a function object created with bind.
\r
984 // bind needs all parameters to match the function's expected types
\r
985 // and all default parameters specified. Some examples:
\r
986 // UIThreadCall(bind(wxBell));
\r
987 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
\r
988 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
\r
991 wxCommandEvent event(wxEVT_UITHREADCALL);
\r
992 event.SetClientData((void*)new boost::function0<void>(fn));
\r
993 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
997 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
\r
999 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
\r
1004 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
\r
1010 void CMainFrame::OnMenuViewShowGenerated(wxCommandEvent& event)
\r
1012 // View->Show Generated
\r
1013 fShowGenerated = event.IsChecked();
\r
1014 CWalletDB().WriteSetting("fShowGenerated", fShowGenerated);
\r
1015 RefreshListCtrl();
\r
1018 void CMainFrame::OnUpdateUIViewShowGenerated(wxUpdateUIEvent& event)
\r
1020 event.Check(fShowGenerated);
\r
1023 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
\r
1025 // Options->Generate Coins
\r
1026 GenerateBitcoins(event.IsChecked());
\r
1029 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
\r
1031 event.Check(fGenerateBitcoins);
\r
1034 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
\r
1036 // Options->Change Your Address
\r
1037 OnButtonChange(event);
\r
1040 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
\r
1042 // Options->Options
\r
1043 COptionsDialog dialog(this);
\r
1044 dialog.ShowModal();
\r
1047 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
\r
1050 CAboutDialog dialog(this);
\r
1051 dialog.ShowModal();
\r
1054 void CMainFrame::OnButtonSend(wxCommandEvent& event)
\r
1057 CSendDialog dialog(this);
\r
1058 dialog.ShowModal();
\r
1061 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
\r
1063 // Toolbar: Address Book
\r
1064 CAddressBookDialog dialogAddr(this, "", false);
\r
1065 if (dialogAddr.ShowModal() == 2)
\r
1068 CSendDialog dialogSend(this, dialogAddr.GetAddress());
\r
1069 dialogSend.ShowModal();
\r
1073 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
\r
1075 // Automatically select-all when entering window
\r
1076 m_textCtrlAddress->SetSelection(-1, -1);
\r
1077 fOnSetFocusAddress = true;
\r
1081 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
\r
1083 if (fOnSetFocusAddress)
\r
1084 m_textCtrlAddress->SetSelection(-1, -1);
\r
1085 fOnSetFocusAddress = false;
\r
1089 void CMainFrame::OnButtonChange(wxCommandEvent& event)
\r
1091 CYourAddressDialog dialog(this, string(m_textCtrlAddress->GetValue()));
\r
1092 if (!dialog.ShowModal())
\r
1094 string strAddress = (string)dialog.GetAddress();
\r
1095 if (strAddress != m_textCtrlAddress->GetValue())
\r
1098 if (!AddressToHash160(strAddress, hash160))
\r
1100 if (!mapPubKeys.count(hash160))
\r
1102 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
\r
1103 m_textCtrlAddress->SetValue(strAddress);
\r
1107 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
\r
1109 // Copy address box to clipboard
\r
1110 if (wxTheClipboard->Open())
\r
1112 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
\r
1113 wxTheClipboard->Close();
\r
1117 void CMainFrame::OnListItemActivated(wxListEvent& event)
\r
1119 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
\r
1121 CRITICAL_BLOCK(cs_mapWallet)
\r
1123 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
\r
1124 if (mi == mapWallet.end())
\r
1126 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
\r
1129 wtx = (*mi).second;
\r
1131 CTxDetailsDialog dialog(this, wtx);
\r
1132 dialog.ShowModal();
\r
1133 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
\r
1134 //pdialog->Show();
\r
1143 //////////////////////////////////////////////////////////////////////////////
\r
1145 // CTxDetailsDialog
\r
1148 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
\r
1150 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1153 strHTML.reserve(4000);
\r
1154 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
\r
1156 int64 nTime = wtx.GetTxTime();
\r
1157 int64 nCredit = wtx.GetCredit();
\r
1158 int64 nDebit = wtx.GetDebit();
\r
1159 int64 nNet = nCredit - nDebit;
\r
1163 strHTML += "<b>Status:</b> " + FormatTxStatus(wtx);
\r
1164 int nRequests = wtx.GetRequestCount();
\r
1165 if (nRequests != -1)
\r
1167 if (nRequests == 0)
\r
1168 strHTML += ", has not been successfully broadcast yet";
\r
1169 else if (nRequests == 1)
\r
1170 strHTML += strprintf(", broadcast through %d node", nRequests);
\r
1172 strHTML += strprintf(", broadcast through %d nodes", nRequests);
\r
1174 strHTML += "<br>";
\r
1176 strHTML += "<b>Date:</b> " + (nTime ? DateTimeStr(nTime) : "") + "<br>";
\r
1182 if (wtx.IsCoinBase())
\r
1184 strHTML += "<b>Source:</b> Generated<br>";
\r
1186 else if (!wtx.mapValue["from"].empty())
\r
1188 // Online transaction
\r
1189 if (!wtx.mapValue["from"].empty())
\r
1190 strHTML += "<b>From:</b> " + HtmlEscape(wtx.mapValue["from"]) + "<br>";
\r
1194 // Offline transaction
\r
1198 foreach(const CTxOut& txout, wtx.vout)
\r
1200 if (txout.IsMine())
\r
1202 vector<unsigned char> vchPubKey;
\r
1203 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
\r
1205 string strAddress = PubKeyToAddress(vchPubKey);
\r
1206 if (mapAddressBook.count(strAddress))
\r
1208 strHTML += "<b>From:</b> unknown<br>";
\r
1209 strHTML += "<b>To:</b> ";
\r
1210 strHTML += HtmlEscape(strAddress);
\r
1211 if (!mapAddressBook[strAddress].empty())
\r
1212 strHTML += " (yours, label: " + mapAddressBook[strAddress] + ")";
\r
1214 strHTML += " (yours)";
\r
1215 strHTML += "<br>";
\r
1228 string strAddress;
\r
1229 if (!wtx.mapValue["to"].empty())
\r
1231 // Online transaction
\r
1232 strAddress = wtx.mapValue["to"];
\r
1233 strHTML += "<b>To:</b> ";
\r
1234 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1235 strHTML += mapAddressBook[strAddress] + " ";
\r
1236 strHTML += HtmlEscape(strAddress) + "<br>";
\r
1243 if (wtx.IsCoinBase() && nCredit == 0)
\r
1248 int64 nUnmatured = 0;
\r
1249 foreach(const CTxOut& txout, wtx.vout)
\r
1250 nUnmatured += txout.GetCredit();
\r
1251 if (wtx.IsInMainChain())
\r
1252 strHTML += strprintf("<b>Credit:</b> (%s matures in %d more blocks)<br>", FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
\r
1254 strHTML += "<b>Credit:</b> (not accepted)<br>";
\r
1256 else if (nNet > 0)
\r
1261 strHTML += "<b>Credit:</b> " + FormatMoney(nNet) + "<br>";
\r
1265 bool fAllFromMe = true;
\r
1266 foreach(const CTxIn& txin, wtx.vin)
\r
1267 fAllFromMe = fAllFromMe && txin.IsMine();
\r
1269 bool fAllToMe = true;
\r
1270 foreach(const CTxOut& txout, wtx.vout)
\r
1271 fAllToMe = fAllToMe && txout.IsMine();
\r
1278 foreach(const CTxOut& txout, wtx.vout)
\r
1280 if (txout.IsMine())
\r
1283 if (wtx.mapValue["to"].empty())
\r
1285 // Offline transaction
\r
1287 if (ExtractHash160(txout.scriptPubKey, hash160))
\r
1289 string strAddress = Hash160ToAddress(hash160);
\r
1290 strHTML += "<b>To:</b> ";
\r
1291 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
\r
1292 strHTML += mapAddressBook[strAddress] + " ";
\r
1293 strHTML += strAddress;
\r
1294 strHTML += "<br>";
\r
1298 strHTML += "<b>Debit:</b> " + FormatMoney(-txout.nValue) + "<br>";
\r
1303 // Payment to self
\r
1304 /// issue: can't tell which is the payment and which is the change anymore
\r
1305 //int64 nValue = wtx.vout[0].nValue;
\r
1306 //strHTML += "<b>Debit:</b> " + FormatMoney(-nValue) + "<br>";
\r
1307 //strHTML += "<b>Credit:</b> " + FormatMoney(nValue) + "<br>";
\r
1310 int64 nTxFee = nDebit - wtx.GetValueOut();
\r
1312 strHTML += "<b>Transaction fee:</b> " + FormatMoney(-nTxFee) + "<br>";
\r
1317 // Mixed debit transaction
\r
1319 foreach(const CTxIn& txin, wtx.vin)
\r
1320 if (txin.IsMine())
\r
1321 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1322 foreach(const CTxOut& txout, wtx.vout)
\r
1323 if (txout.IsMine())
\r
1324 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1328 strHTML += "<b>Net amount:</b> " + FormatMoney(nNet, true) + "<br>";
\r
1334 if (!wtx.mapValue["message"].empty())
\r
1335 strHTML += "<br><b>Message:</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
\r
1337 if (wtx.IsCoinBase())
\r
1338 strHTML += "<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
1346 strHTML += "<hr><br>debug print<br><br>";
\r
1347 foreach(const CTxIn& txin, wtx.vin)
\r
1348 if (txin.IsMine())
\r
1349 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
\r
1350 foreach(const CTxOut& txout, wtx.vout)
\r
1351 if (txout.IsMine())
\r
1352 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
\r
1354 strHTML += "<b>Inputs:</b><br>";
\r
1355 CRITICAL_BLOCK(cs_mapWallet)
\r
1357 foreach(const CTxIn& txin, wtx.vin)
\r
1359 COutPoint prevout = txin.prevout;
\r
1360 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
\r
1361 if (mi != mapWallet.end())
\r
1363 const CWalletTx& prev = (*mi).second;
\r
1364 if (prevout.n < prev.vout.size())
\r
1366 strHTML += HtmlEscape(prev.ToString(), true);
\r
1367 strHTML += " " + FormatTxStatus(prev) + ", ";
\r
1368 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
\r
1374 strHTML += "<br><hr><br><b>Transaction:</b><br>";
\r
1375 strHTML += HtmlEscape(wtx.ToString(), true);
\r
1380 strHTML += "</font></html>";
\r
1381 string(strHTML.begin(), strHTML.end()).swap(strHTML);
\r
1382 m_htmlWin->SetPage(strHTML);
\r
1383 m_buttonOK->SetFocus();
\r
1387 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
\r
1397 //////////////////////////////////////////////////////////////////////////////
\r
1402 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
\r
1404 // Set up list box of page choices
\r
1405 m_listBox->Append("Main");
\r
1406 //m_listBox->Append("Test 2");
\r
1407 m_listBox->SetSelection(0);
\r
1410 m_checkBoxMinimizeOnClose->SetLabel("&Minimize on close");
\r
1411 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
\r
1415 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
\r
1416 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
\r
1417 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
\r
1418 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
\r
1419 int nProcessors = wxThread::GetCPUCount();
\r
1420 if (nProcessors < 1)
\r
1421 nProcessors = 999;
\r
1422 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
\r
1423 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
\r
1424 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
\r
1425 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
\r
1426 m_checkBoxUseProxy->SetValue(fUseProxy);
\r
1427 m_textCtrlProxyIP->Enable(fUseProxy);
\r
1428 m_textCtrlProxyPort->Enable(fUseProxy);
\r
1429 m_staticTextProxyIP->Enable(fUseProxy);
\r
1430 m_staticTextProxyPort->Enable(fUseProxy);
\r
1431 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
\r
1432 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
\r
1434 m_buttonOK->SetFocus();
\r
1437 void COptionsDialog::SelectPage(int nPage)
\r
1439 m_panelMain->Show(nPage == 0);
\r
1440 m_panelTest2->Show(nPage == 1);
\r
1442 m_scrolledWindow->Layout();
\r
1443 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
\r
1446 void COptionsDialog::OnListBox(wxCommandEvent& event)
\r
1448 SelectPage(event.GetSelection());
\r
1451 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
\r
1453 int64 nTmp = nTransactionFee;
\r
1454 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
\r
1455 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
\r
1458 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
\r
1460 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
\r
1463 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
\r
1465 m_textCtrlProxyIP->Enable(event.IsChecked());
\r
1466 m_textCtrlProxyPort->Enable(event.IsChecked());
\r
1467 m_staticTextProxyIP->Enable(event.IsChecked());
\r
1468 m_staticTextProxyPort->Enable(event.IsChecked());
\r
1471 CAddress COptionsDialog::GetProxyAddr()
\r
1473 // Be careful about byte order, addr.ip and addr.port are big endian
\r
1474 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
\r
1475 if (addr.ip == INADDR_NONE)
\r
1476 addr.ip = addrProxy.ip;
\r
1477 int nPort = atoi(m_textCtrlProxyPort->GetValue());
\r
1478 addr.port = htons(nPort);
\r
1479 if (nPort <= 0 || nPort > USHRT_MAX)
\r
1480 addr.port = addrProxy.port;
\r
1484 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
\r
1486 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
\r
1487 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
\r
1491 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
\r
1493 OnButtonApply(event);
\r
1497 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
\r
1502 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
\r
1504 CWalletDB walletdb;
\r
1506 int64 nPrevTransactionFee = nTransactionFee;
\r
1507 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
\r
1508 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
\r
1510 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
\r
1511 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
\r
1513 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
\r
1514 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
\r
1516 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
\r
1518 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
\r
1519 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
\r
1521 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
\r
1522 GenerateBitcoins(fGenerateBitcoins);
\r
1524 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
\r
1526 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
\r
1527 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
\r
1530 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
\r
1532 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
\r
1533 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
\r
1534 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
1537 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
\r
1539 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
\r
1540 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
\r
1543 fUseProxy = m_checkBoxUseProxy->GetValue();
\r
1544 walletdb.WriteSetting("fUseProxy", fUseProxy);
\r
1546 addrProxy = GetProxyAddr();
\r
1547 walletdb.WriteSetting("addrProxy", addrProxy);
\r
1555 //////////////////////////////////////////////////////////////////////////////
\r
1560 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
\r
1562 m_staticTextVersion->SetLabel(strprintf("version 0.%d.%d beta", VERSION/100, VERSION%100));
\r
1564 #if !wxUSE_UNICODE
\r
1565 // Workaround until upgrade to wxWidgets supporting UTF-8
\r
1566 // Hack to change the (c) character from UTF-8 back to ANSI
\r
1567 wxString str = m_staticTextMain->GetLabel();
\r
1568 if (str.Find('\xC2') != wxNOT_FOUND)
\r
1569 str.Remove(str.Find('\xC2'), 1);
\r
1570 m_staticTextMain->SetLabel(str);
\r
1573 // Resize on Linux to make the window fit the text.
\r
1574 // The text was wrapped manually rather than using the Wrap setting because
\r
1575 // the wrap would be too small on Linux and it can't be changed at this point.
\r
1576 wxFont fontTmp = m_staticTextMain->GetFont();
\r
1577 if (fontTmp.GetPointSize() > 8);
\r
1578 fontTmp.SetPointSize(8);
\r
1579 m_staticTextMain->SetFont(fontTmp);
\r
1580 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() - 4);
\r
1584 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
\r
1594 //////////////////////////////////////////////////////////////////////////////
\r
1599 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
\r
1602 m_textCtrlAddress->SetValue(strAddress);
\r
1603 m_choiceTransferType->SetSelection(0);
\r
1604 m_bitmapCheckMark->Show(false);
\r
1605 fEnabledPrev = true;
\r
1606 m_textCtrlAddress->SetFocus();
\r
1607 //// todo: should add a display of your balance for convenience
\r
1609 wxFont fontTmp = m_staticTextInstructions->GetFont();
\r
1610 if (fontTmp.GetPointSize() > 9);
\r
1611 fontTmp.SetPointSize(9);
\r
1612 m_staticTextInstructions->SetFont(fontTmp);
\r
1613 SetSize(725, 380);
\r
1618 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
\r
1619 SetIcon(iconSend);
\r
1621 wxCommandEvent event;
\r
1622 OnTextAddress(event);
\r
1624 // Fixup the tab order
\r
1625 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
\r
1626 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
\r
1630 void CSendDialog::OnTextAddress(wxCommandEvent& event)
\r
1633 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
\r
1634 m_bitmapCheckMark->Show(fBitcoinAddress);
\r
1636 // Grey out message if bitcoin address
\r
1637 bool fEnable = !fBitcoinAddress;
\r
1638 m_staticTextFrom->Enable(fEnable);
\r
1639 m_textCtrlFrom->Enable(fEnable);
\r
1640 m_staticTextMessage->Enable(fEnable);
\r
1641 m_textCtrlMessage->Enable(fEnable);
\r
1642 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
\r
1643 if (!fEnable && fEnabledPrev)
\r
1645 strFromSave = m_textCtrlFrom->GetValue();
\r
1646 strMessageSave = m_textCtrlMessage->GetValue();
\r
1647 m_textCtrlFrom->SetValue("Will appear as \"From: Unknown\"");
\r
1648 m_textCtrlMessage->SetValue("Can't include a message when sending to a Bitcoin address");
\r
1650 else if (fEnable && !fEnabledPrev)
\r
1652 m_textCtrlFrom->SetValue(strFromSave);
\r
1653 m_textCtrlMessage->SetValue(strMessageSave);
\r
1655 fEnabledPrev = fEnable;
\r
1658 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
\r
1660 // Reformat the amount
\r
1661 if (m_textCtrlAmount->GetValue().Trim().empty())
\r
1664 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
\r
1665 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
\r
1668 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
\r
1670 // Open address book
\r
1671 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), true);
\r
1672 if (dialog.ShowModal())
\r
1673 m_textCtrlAddress->SetValue(dialog.GetAddress());
\r
1676 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
\r
1678 // Copy clipboard to address box
\r
1679 if (wxTheClipboard->Open())
\r
1681 if (wxTheClipboard->IsSupported(wxDF_TEXT))
\r
1683 wxTextDataObject data;
\r
1684 wxTheClipboard->GetData(data);
\r
1685 m_textCtrlAddress->SetValue(data.GetText());
\r
1687 wxTheClipboard->Close();
\r
1691 void CSendDialog::OnButtonSend(wxCommandEvent& event)
\r
1694 string strAddress = (string)m_textCtrlAddress->GetValue();
\r
1698 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
\r
1700 wxMessageBox("Error in amount ", "Send Coins");
\r
1703 if (nValue > GetBalance())
\r
1705 wxMessageBox("Amount exceeds your balance ", "Send Coins");
\r
1708 if (nValue + nTransactionFee > GetBalance())
\r
1710 wxMessageBox(string("Total exceeds your balance when the ") + FormatMoney(nTransactionFee) + " transaction fee is included ", "Send Coins");
\r
1714 // Parse bitcoin address
\r
1716 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
\r
1718 if (fBitcoinAddress)
\r
1720 // Send to bitcoin address
\r
1721 CScript scriptPubKey;
\r
1722 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
\r
1724 string strError = SendMoney(scriptPubKey, nValue, wtx);
\r
1725 if (strError != "")
\r
1726 wxMessageBox(strError + " ", "Sending...");
\r
1728 wxMessageBox("Payment sent ", "Sending...");
\r
1732 // Parse IP address
\r
1733 CAddress addr(strAddress);
\r
1734 if (!addr.IsValid())
\r
1736 wxMessageBox("Invalid address ", "Send Coins");
\r
1741 wtx.mapValue["to"] = strAddress;
\r
1742 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
\r
1743 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
\r
1745 // Send to IP address
\r
1746 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
\r
1747 if (!pdialog->ShowModal())
\r
1751 CRITICAL_BLOCK(cs_mapAddressBook)
\r
1752 if (!mapAddressBook.count(strAddress))
\r
1753 SetAddressBookName(strAddress, "");
\r
1758 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
\r
1769 //////////////////////////////////////////////////////////////////////////////
\r
1774 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
1777 nPrice = nPriceIn;
\r
1779 start = wxDateTime::UNow();
\r
1780 memset(pszStatus, 0, sizeof(pszStatus));
\r
1781 fCanCancel = true;
\r
1785 fWorkDone = false;
\r
1787 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
\r
1790 SetTitle(strprintf("Sending %s to %s", FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
\r
1791 m_textCtrlStatus->SetValue("");
\r
1793 CreateThread(SendingDialogStartTransfer, this);
\r
1796 CSendingDialog::~CSendingDialog()
\r
1798 printf("~CSendingDialog()\n");
\r
1801 void CSendingDialog::Close()
\r
1803 // Last one out turn out the lights.
\r
1804 // fWorkDone signals that work side is done and UI thread should call destroy.
\r
1805 // fUIDone signals that UI window has closed and work thread should call destroy.
\r
1806 // This allows the window to disappear and end modality when cancelled
\r
1807 // without making the user wait for ConnectNode to return. The dialog object
\r
1808 // hangs around in the background until the work thread exits.
\r
1810 EndModal(fSuccess);
\r
1819 void CSendingDialog::OnClose(wxCloseEvent& event)
\r
1821 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
\r
1828 wxCommandEvent cmdevent;
\r
1829 OnButtonCancel(cmdevent);
\r
1833 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
\r
1839 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
\r
1845 void CSendingDialog::OnPaint(wxPaintEvent& event)
\r
1847 if (strlen(pszStatus) > 130)
\r
1848 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
\r
1850 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
\r
1851 m_staticTextSending->SetFocus();
\r
1853 m_buttonCancel->Enable(false);
\r
1856 m_buttonOK->Enable(true);
\r
1857 m_buttonOK->SetFocus();
\r
1858 m_buttonCancel->Enable(false);
\r
1860 if (fAbort && fCanCancel && IsShown())
\r
1862 strcpy(pszStatus, "CANCELLED");
\r
1863 m_buttonOK->Enable(true);
\r
1864 m_buttonOK->SetFocus();
\r
1865 m_buttonCancel->Enable(false);
\r
1866 m_buttonCancel->SetLabel("Cancelled");
\r
1868 wxMessageBox("Transfer cancelled ", "Sending...", wxOK, this);
\r
1875 // Everything from here on is not in the UI thread and must only communicate
\r
1876 // with the rest of the dialog through variables and calling repaint.
\r
1879 void CSendingDialog::Repaint()
\r
1882 wxPaintEvent event;
\r
1883 GetEventHandler()->AddPendingEvent(event);
\r
1886 bool CSendingDialog::Status()
\r
1893 if (fAbort && fCanCancel)
\r
1895 memset(pszStatus, 0, 10);
\r
1896 strcpy(pszStatus, "CANCELLED");
\r
1904 bool CSendingDialog::Status(const string& str)
\r
1909 // This can be read by the UI thread at any time,
\r
1910 // so copy in a way that can be read cleanly at all times.
\r
1911 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
\r
1912 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
\r
1918 bool CSendingDialog::Error(const string& str)
\r
1920 fCanCancel = false;
\r
1922 Status(string("Error: ") + str);
\r
1926 void SendingDialogStartTransfer(void* parg)
\r
1928 ((CSendingDialog*)parg)->StartTransfer();
\r
1931 void CSendingDialog::StartTransfer()
\r
1933 // Make sure we have enough money
\r
1934 if (nPrice + nTransactionFee > GetBalance())
\r
1936 Error("You don't have enough money");
\r
1940 // We may have connected already for product details
\r
1941 if (!Status("Connecting..."))
\r
1943 CNode* pnode = ConnectNode(addr, 15 * 60);
\r
1946 Error("Unable to connect");
\r
1950 // Send order to seller, with response going to OnReply2 via event handler
\r
1951 if (!Status("Requesting public key..."))
\r
1953 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
\r
1956 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
\r
1958 ((CSendingDialog*)parg)->OnReply2(vRecv);
\r
1961 void CSendingDialog::OnReply2(CDataStream& vRecv)
\r
1963 if (!Status("Received public key..."))
\r
1966 CScript scriptPubKey;
\r
1973 string strMessage;
\r
1974 vRecv >> strMessage;
\r
1975 Error("Transfer was not accepted");
\r
1976 //// todo: enlarge the window and enable a hidden white box to put seller's message
\r
1979 vRecv >> scriptPubKey;
\r
1983 //// what do we want to do about this?
\r
1984 Error("Invalid response received");
\r
1988 // Pause to give the user a chance to cancel
\r
1989 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
\r
1996 CRITICAL_BLOCK(cs_main)
\r
1999 if (!Status("Creating transaction..."))
\r
2001 if (nPrice + nTransactionFee > GetBalance())
\r
2003 Error("You don't have enough money");
\r
2007 int64 nFeeRequired;
\r
2008 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
\r
2010 if (nPrice + nFeeRequired > GetBalance())
\r
2011 Error(strprintf("This is an oversized transaction that requires a transaction fee of %s", FormatMoney(nFeeRequired).c_str()));
\r
2013 Error("Transaction creation failed");
\r
2017 // Make sure we're still connected
\r
2018 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
\r
2021 Error("Lost connection, transaction cancelled");
\r
2025 // Last chance to cancel
\r
2029 fCanCancel = false;
\r
2032 fCanCancel = true;
\r
2035 fCanCancel = false;
\r
2037 if (!Status("Sending payment..."))
\r
2041 if (!CommitTransactionSpent(wtx, key))
\r
2043 Error("Error finalizing payment");
\r
2047 // Send payment tx to seller, with response going to OnReply3 via event handler
\r
2048 pnode->PushRequest("submitorder", wtx, SendingDialogOnReply3, this);
\r
2050 // Accept and broadcast transaction
\r
2051 if (!wtx.AcceptTransaction())
\r
2052 printf("ERROR: CSendingDialog : wtxNew.AcceptTransaction() %s failed\n", wtx.GetHash().ToString().c_str());
\r
2053 wtx.RelayWalletTransaction();
\r
2055 Status("Waiting for confirmation...");
\r
2056 MainFrameRepaint();
\r
2060 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
\r
2062 ((CSendingDialog*)parg)->OnReply3(vRecv);
\r
2065 void CSendingDialog::OnReply3(CDataStream& vRecv)
\r
2073 Error("The payment was sent, but the recipient was unable to verify it.\n"
\r
2074 "The transaction is recorded and will credit to the recipient,\n"
\r
2075 "but the comment information will be blank.");
\r
2081 //// what do we want to do about this?
\r
2082 Error("Payment was sent, but an invalid response was received");
\r
2088 Status("Payment completed");
\r
2096 //////////////////////////////////////////////////////////////////////////////
\r
2098 // CYourAddressDialog
\r
2101 CYourAddressDialog::CYourAddressDialog(wxWindow* parent, const string& strInitSelected) : CYourAddressDialogBase(parent)
\r
2103 // Init column headers
\r
2104 m_listCtrl->InsertColumn(0, "Label", wxLIST_FORMAT_LEFT, 200);
\r
2105 m_listCtrl->InsertColumn(1, "Bitcoin Address", wxLIST_FORMAT_LEFT, 350);
\r
2106 m_listCtrl->SetFocus();
\r
2108 // Fill listctrl with address book data
\r
2109 CRITICAL_BLOCK(cs_mapKeys)
\r
2110 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2112 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2114 string strAddress = item.first;
\r
2115 string strName = item.second;
\r
2117 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2120 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2121 if (strAddress == strInitSelected)
\r
2122 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2128 wxString CYourAddressDialog::GetAddress()
\r
2130 int nIndex = GetSelection(m_listCtrl);
\r
2133 return GetItemText(m_listCtrl, nIndex, 1);
\r
2136 void CYourAddressDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2138 // Update address book with edited name
\r
2139 if (event.IsEditCancelled())
\r
2141 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2142 SetAddressBookName(strAddress, string(event.GetText()));
\r
2143 pframeMain->RefreshListCtrl();
\r
2146 void CYourAddressDialog::OnListItemSelected(wxListEvent& event)
\r
2150 void CYourAddressDialog::OnListItemActivated(wxListEvent& event)
\r
2152 // Doubleclick edits item
\r
2153 wxCommandEvent event2;
\r
2154 OnButtonRename(event2);
\r
2157 void CYourAddressDialog::OnButtonRename(wxCommandEvent& event)
\r
2160 int nIndex = GetSelection(m_listCtrl);
\r
2163 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2164 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2165 CGetTextFromUserDialog dialog(this, "Edit Address Label", "New Label", strName);
\r
2166 if (!dialog.ShowModal())
\r
2168 strName = dialog.GetValue();
\r
2171 SetAddressBookName(strAddress, strName);
\r
2172 m_listCtrl->SetItemText(nIndex, strName);
\r
2173 pframeMain->RefreshListCtrl();
\r
2176 void CYourAddressDialog::OnButtonNew(wxCommandEvent& event)
\r
2179 CGetTextFromUserDialog dialog(this, "New Bitcoin Address", "Label", "");
\r
2180 if (!dialog.ShowModal())
\r
2182 string strName = dialog.GetValue();
\r
2184 // Generate new key
\r
2185 string strAddress = PubKeyToAddress(GenerateNewKey());
\r
2186 SetAddressBookName(strAddress, strName);
\r
2188 // Add to list and select it
\r
2189 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2190 SetSelection(m_listCtrl, nIndex);
\r
2191 m_listCtrl->SetFocus();
\r
2194 void CYourAddressDialog::OnButtonCopy(wxCommandEvent& event)
\r
2196 // Copy address box to clipboard
\r
2197 if (wxTheClipboard->Open())
\r
2199 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2200 wxTheClipboard->Close();
\r
2204 void CYourAddressDialog::OnButtonOK(wxCommandEvent& event)
\r
2210 void CYourAddressDialog::OnButtonCancel(wxCommandEvent& event)
\r
2216 void CYourAddressDialog::OnClose(wxCloseEvent& event)
\r
2227 //////////////////////////////////////////////////////////////////////////////
\r
2229 // CAddressBookDialog
\r
2232 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, bool fSendingIn) : CAddressBookDialogBase(parent)
\r
2234 fSending = fSendingIn;
\r
2236 m_buttonCancel->Show(false);
\r
2238 // Init column headers
\r
2239 m_listCtrl->InsertColumn(0, "Name", wxLIST_FORMAT_LEFT, 200);
\r
2240 m_listCtrl->InsertColumn(1, "Address", wxLIST_FORMAT_LEFT, 350);
\r
2241 m_listCtrl->SetFocus();
\r
2244 wxIcon iconAddressBook;
\r
2245 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
\r
2246 SetIcon(iconAddressBook);
\r
2248 // Fill listctrl with address book data
\r
2249 CRITICAL_BLOCK(cs_mapKeys)
\r
2250 CRITICAL_BLOCK(cs_mapAddressBook)
\r
2252 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
\r
2254 string strAddress = item.first;
\r
2255 string strName = item.second;
\r
2257 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2260 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2261 if (strAddress == strInitSelected)
\r
2262 m_listCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
\r
2268 wxString CAddressBookDialog::GetAddress()
\r
2270 int nIndex = GetSelection(m_listCtrl);
\r
2273 return GetItemText(m_listCtrl, nIndex, 1);
\r
2276 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
\r
2278 // Update address book with edited name
\r
2279 if (event.IsEditCancelled())
\r
2281 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
\r
2282 SetAddressBookName(strAddress, string(event.GetText()));
\r
2283 pframeMain->RefreshListCtrl();
\r
2286 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
\r
2290 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
\r
2294 // Doubleclick returns selection
\r
2295 EndModal(GetAddress() != "" ? 2 : 0);
\r
2299 // Doubleclick edits item
\r
2300 wxCommandEvent event2;
\r
2301 OnButtonEdit(event2);
\r
2305 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
\r
2308 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
\r
2310 wxMessageBox("This is one of your own addresses for receiving payments and cannot be entered in the address book. ", strTitle);
\r
2314 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
\r
2317 int nIndex = GetSelection(m_listCtrl);
\r
2320 string strName = (string)m_listCtrl->GetItemText(nIndex);
\r
2321 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2322 string strAddressOrg = strAddress;
\r
2325 CGetTextFromUserDialog dialog(this, "Edit Address", "Name", strName, "Address", strAddress);
\r
2326 if (!dialog.ShowModal())
\r
2328 strName = dialog.GetValue1();
\r
2329 strAddress = dialog.GetValue2();
\r
2331 while (CheckIfMine(strAddress, "Edit Address"));
\r
2334 if (strAddress != strAddressOrg)
\r
2335 CWalletDB().EraseName(strAddressOrg);
\r
2336 SetAddressBookName(strAddress, strName);
\r
2337 m_listCtrl->SetItem(nIndex, 1, strAddress);
\r
2338 m_listCtrl->SetItemText(nIndex, strName);
\r
2339 pframeMain->RefreshListCtrl();
\r
2342 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
\r
2346 string strAddress;
\r
2349 CGetTextFromUserDialog dialog(this, "New Address", "Name", strName, "Address", strAddress);
\r
2350 if (!dialog.ShowModal())
\r
2352 strName = dialog.GetValue1();
\r
2353 strAddress = dialog.GetValue2();
\r
2355 while (CheckIfMine(strAddress, "New Address"));
\r
2357 // Add to list and select it
\r
2358 SetAddressBookName(strAddress, strName);
\r
2359 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
\r
2360 SetSelection(m_listCtrl, nIndex);
\r
2361 m_listCtrl->SetFocus();
\r
2362 pframeMain->RefreshListCtrl();
\r
2365 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
\r
2367 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
\r
2369 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
\r
2371 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
\r
2372 CWalletDB().EraseName(strAddress);
\r
2373 m_listCtrl->DeleteItem(nIndex);
\r
2376 pframeMain->RefreshListCtrl();
\r
2379 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
\r
2381 // Copy address box to clipboard
\r
2382 if (wxTheClipboard->Open())
\r
2384 wxTheClipboard->SetData(new wxTextDataObject(GetAddress()));
\r
2385 wxTheClipboard->Close();
\r
2389 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
\r
2392 EndModal(GetAddress() != "" ? 1 : 0);
\r
2395 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
\r
2401 void CAddressBookDialog::OnClose(wxCloseEvent& event)
\r
2412 //////////////////////////////////////////////////////////////////////////////
\r
2419 ID_TASKBAR_RESTORE = 10001,
\r
2420 ID_TASKBAR_OPTIONS,
\r
2421 ID_TASKBAR_GENERATE,
\r
2425 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
\r
2426 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
\r
2427 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
\r
2428 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
\r
2429 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
\r
2430 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
\r
2431 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
\r
2434 void CMyTaskBarIcon::Show(bool fShow)
\r
2436 static char pszPrevTip[200];
\r
2439 string strTooltip = "Bitcoin";
\r
2440 if (fGenerateBitcoins)
\r
2441 strTooltip = "Bitcoin - Generating";
\r
2442 if (fGenerateBitcoins && vNodes.empty())
\r
2443 strTooltip = "Bitcoin - (not connected)";
\r
2445 // Optimization, only update when changed, using char array to be reentrant
\r
2446 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
\r
2448 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
\r
2450 SetIcon(wxICON(bitcoin), strTooltip);
\r
2452 SetIcon(bitcoin20_xpm, strTooltip);
\r
2458 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
\r
2463 void CMyTaskBarIcon::Hide()
\r
2468 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
\r
2473 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
\r
2478 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
\r
2480 // Since it's modal, get the main window to do it
\r
2481 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_MENUOPTIONSOPTIONS);
\r
2482 pframeMain->GetEventHandler()->AddPendingEvent(event2);
\r
2485 void CMyTaskBarIcon::Restore()
\r
2487 pframeMain->Show();
\r
2488 wxIconizeEvent event(0, false);
\r
2489 pframeMain->GetEventHandler()->AddPendingEvent(event);
\r
2490 pframeMain->Iconize(false);
\r
2491 pframeMain->Raise();
\r
2494 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
\r
2496 GenerateBitcoins(event.IsChecked());
\r
2499 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
\r
2501 event.Check(fGenerateBitcoins);
\r
2504 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
\r
2506 pframeMain->Close(true);
\r
2509 void CMyTaskBarIcon::UpdateTooltip()
\r
2511 if (IsIconInstalled())
\r
2515 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
\r
2517 wxMenu* pmenu = new wxMenu;
\r
2518 pmenu->Append(ID_TASKBAR_RESTORE, "&Open Bitcoin");
\r
2519 pmenu->Append(ID_TASKBAR_OPTIONS, "O&ptions...");
\r
2520 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, "&Generate Coins")->Check(fGenerateBitcoins);
\r
2521 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
\r
2522 pmenu->AppendSeparator();
\r
2523 pmenu->Append(ID_TASKBAR_EXIT, "E&xit");
\r
2537 //////////////////////////////////////////////////////////////////////////////
\r
2542 // Define a new application
\r
2543 class CMyApp: public wxApp
\r
2552 // 2nd-level exception handling: we get all the exceptions occurring in any
\r
2553 // event handler here
\r
2554 virtual bool OnExceptionInMainLoop();
\r
2556 // 3rd, and final, level exception handling: whenever an unhandled
\r
2557 // exception is caught, this function is called
\r
2558 virtual void OnUnhandledException();
\r
2560 // and now for something different: this function is called in case of a
\r
2561 // crash (e.g. dereferencing null pointer, division by 0, ...)
\r
2562 virtual void OnFatalException();
\r
2565 IMPLEMENT_APP(CMyApp)
\r
2567 bool CMyApp::OnInit()
\r
2569 bool fRet = false;
\r
2574 catch (std::exception& e) {
\r
2575 PrintException(&e, "OnInit()");
\r
2577 PrintException(NULL, "OnInit()");
\r
2584 bool CMyApp::OnInit2()
\r
2587 // Turn off microsoft heap dump noise for now
\r
2588 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
\r
2589 _CrtSetReportFile(_CRT_WARN, CreateFile("NUL", GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, 0));
\r
2591 #if defined(__WXMSW__) && defined(__WXDEBUG__)
\r
2592 // Disable malfunctioning wxWidgets debug assertion
\r
2593 g_isPainting = 10000;
\r
2595 wxImage::AddHandler(new wxPNGHandler);
\r
2597 SetAppName("Bitcoin");
\r
2599 SetAppName("bitcoin");
\r
2606 if (argc > 1 && argv[1][0] != '-' && argv[1][0] != '/')
\r
2608 int ret = CommandLineRPC(argc, argv);
\r
2612 ParseParameters(argc, argv);
\r
2613 if (mapArgs.count("-?") || mapArgs.count("--help"))
\r
2617 "Usage: bitcoin [options]\t\t\t\t\t\t\n"
\r
2619 " -gen\t\t Generate coins\n"
\r
2620 " -gen=0\t\t Don't generate coins\n"
\r
2621 " -min\t\t Start minimized\n"
\r
2622 " -datadir=<dir>\t Specify data directory\n"
\r
2623 " -proxy=<ip:port>\t Connect through socks4 proxy\n"
\r
2624 " -addnode=<ip>\t Add a node to connect to\n"
\r
2625 " -connect=<ip>\t Connect only to the specified node\n"
\r
2626 " -?\t\t This help message\n";
\r
2627 wxMessageBox(strUsage, "Bitcoin", wxOK);
\r
2630 "Usage: bitcoin [options]\n"
\r
2632 " -gen Generate coins\n"
\r
2633 " -gen=0 Don't generate coins\n"
\r
2634 " -min Start minimized\n"
\r
2635 " -datadir=<dir> Specify data directory\n"
\r
2636 " -proxy=<ip:port> Connect through socks4 proxy\n"
\r
2637 " -addnode=<ip> Add a node to connect to\n"
\r
2638 " -connect=<ip> Connect only to the specified node\n"
\r
2639 " -? This help message\n";
\r
2640 fprintf(stderr, "%s", strUsage.c_str());
\r
2645 if (mapArgs.count("-datadir"))
\r
2646 strlcpy(pszSetDataDir, mapArgs["-datadir"].c_str(), sizeof(pszSetDataDir));
\r
2648 if (mapArgs.count("-debug"))
\r
2651 if (mapArgs.count("-printtodebugger"))
\r
2652 fPrintToDebugger = true;
\r
2654 if (mapArgs.count("-daemon") || mapArgs.count("-d"))
\r
2657 /// todo: need to fork
\r
2658 /// should it fork after the bind/single instance stuff?
\r
2661 if (!fDebug && !pszSetDataDir[0])
\r
2662 ShrinkDebugFile();
\r
2663 printf("\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n");
\r
2664 printf("Bitcoin version %d%s, OS version %s\n", VERSION, pszSubVer, ((string)wxGetOsDescription()).c_str());
\r
2666 if (mapArgs.count("-loadblockindextest"))
\r
2669 txdb.LoadBlockIndex();
\r
2675 // Limit to single instance per user
\r
2676 // Required to protect the database files if we're going to keep deleting log.*
\r
2679 // todo: wxSingleInstanceChecker wasn't working on Linux, never deleted its lock file
\r
2680 // maybe should go by whether successfully bind port 8333 instead
\r
2681 wxString strMutexName = wxString("bitcoin_running.") + getenv("HOMEPATH");
\r
2682 for (int i = 0; i < strMutexName.size(); i++)
\r
2683 if (!isalnum(strMutexName[i]))
\r
2684 strMutexName[i] = '.';
\r
2685 wxSingleInstanceChecker* psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
2686 if (psingleinstancechecker->IsAnotherRunning())
\r
2688 printf("Existing instance found\n");
\r
2689 unsigned int nStart = GetTime();
\r
2692 // TODO: find out how to do this in Linux, or replace with wxWidgets commands
\r
2693 // Show the previous instance and exit
\r
2694 HWND hwndPrev = FindWindow("wxWindowClassNR", "Bitcoin");
\r
2697 if (IsIconic(hwndPrev))
\r
2698 ShowWindow(hwndPrev, SW_RESTORE);
\r
2699 SetForegroundWindow(hwndPrev);
\r
2703 if (GetTime() > nStart + 60)
\r
2706 // Resume this instance if the other exits
\r
2707 delete psingleinstancechecker;
\r
2709 psingleinstancechecker = new wxSingleInstanceChecker(strMutexName);
\r
2710 if (!psingleinstancechecker->IsAnotherRunning())
\r
2716 // Bind to the port early so we can tell if another instance is already running.
\r
2717 // This is a backup to wxSingleInstanceChecker, which doesn't work on Linux.
\r
2719 if (!BindListenPort(strErrors))
\r
2721 wxMessageBox(strErrors, "Bitcoin");
\r
2726 // Load data files
\r
2732 printf("Loading addresses...\n");
\r
2733 nStart = GetTimeMillis();
\r
2734 if (!LoadAddresses())
\r
2735 strErrors += "Error loading addr.dat \n";
\r
2736 printf(" addresses %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
2738 printf("Loading block index...\n");
\r
2739 nStart = GetTimeMillis();
\r
2740 if (!LoadBlockIndex())
\r
2741 strErrors += "Error loading blkindex.dat \n";
\r
2742 printf(" block index %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
2744 printf("Loading wallet...\n");
\r
2745 nStart = GetTimeMillis();
\r
2746 if (!LoadWallet(fFirstRun))
\r
2747 strErrors += "Error loading wallet.dat \n";
\r
2748 printf(" wallet %15"PRI64d"ms\n", GetTimeMillis() - nStart);
\r
2750 printf("Done loading\n");
\r
2753 printf("mapBlockIndex.size() = %d\n", mapBlockIndex.size());
\r
2754 printf("nBestHeight = %d\n", nBestHeight);
\r
2755 printf("mapKeys.size() = %d\n", mapKeys.size());
\r
2756 printf("mapPubKeys.size() = %d\n", mapPubKeys.size());
\r
2757 printf("mapWallet.size() = %d\n", mapWallet.size());
\r
2758 printf("mapAddressBook.size() = %d\n", mapAddressBook.size());
\r
2760 if (!strErrors.empty())
\r
2762 wxMessageBox(strErrors, "Bitcoin");
\r
2766 // Add wallet transactions that aren't already in a block to mapTransactions
\r
2767 ReacceptWalletTransactions();
\r
2772 if (mapArgs.count("-printblockindex") || mapArgs.count("-printblocktree"))
\r
2778 if (mapArgs.count("-printblock"))
\r
2780 string strMatch = mapArgs["-printblock"];
\r
2782 for (map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.begin(); mi != mapBlockIndex.end(); ++mi)
\r
2784 uint256 hash = (*mi).first;
\r
2785 if (strncmp(hash.ToString().c_str(), strMatch.c_str(), strMatch.size()) == 0)
\r
2787 CBlockIndex* pindex = (*mi).second;
\r
2789 block.ReadFromDisk(pindex);
\r
2790 block.BuildMerkleTree();
\r
2797 printf("No blocks matching %s were found\n", strMatch.c_str());
\r
2801 if (mapArgs.count("-gen"))
\r
2803 if (mapArgs["-gen"].empty())
\r
2804 fGenerateBitcoins = true;
\r
2806 fGenerateBitcoins = (atoi(mapArgs["-gen"].c_str()) != 0);
\r
2809 if (mapArgs.count("-proxy"))
\r
2812 addrProxy = CAddress(mapArgs["-proxy"]);
\r
2813 if (!addrProxy.IsValid())
\r
2815 wxMessageBox("Invalid -proxy address", "Bitcoin");
\r
2820 if (mapArgs.count("-addnode"))
\r
2822 foreach(string strAddr, mapMultiArgs["-addnode"])
\r
2824 CAddress addr(strAddr, NODE_NETWORK);
\r
2825 addr.nTime = 0; // so it won't relay unless successfully connected
\r
2826 if (addr.IsValid())
\r
2832 // Create the main frame window
\r
2836 pframeMain = new CMainFrame(NULL);
\r
2837 if (mapArgs.count("-min"))
\r
2838 pframeMain->Iconize(true);
\r
2839 pframeMain->Show(true); // have to show first to get taskbar button to hide
\r
2840 if (fMinimizeToTray && pframeMain->IsIconized())
\r
2841 fClosedToTray = true;
\r
2842 pframeMain->Show(!fClosedToTray);
\r
2843 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
\r
2845 CreateThread(ThreadDelayedRepaint, NULL);
\r
2848 if (!CheckDiskSpace())
\r
2851 RandAddSeedPerfmon();
\r
2853 if (!CreateThread(StartNode, NULL))
\r
2854 wxMessageBox("Error: CreateThread(StartNode) failed", "Bitcoin");
\r
2856 if (mapArgs.count("-server") || fDaemon)
\r
2857 CreateThread(ThreadRPCServer, NULL);
\r
2860 SetStartOnSystemStartup(true);
\r
2867 if (argc >= 2 && stricmp(argv[1], "-send") == 0)
\r
2869 if (argc >= 2 && strcmp(argv[1], "-send") == 0)
\r
2874 ParseMoney(argv[2], nValue);
\r
2876 string strAddress;
\r
2878 strAddress = argv[3];
\r
2879 CAddress addr(strAddress);
\r
2882 wtx.mapValue["to"] = strAddress;
\r
2883 wtx.mapValue["from"] = addrLocalHost.ToString();
\r
2884 wtx.mapValue["message"] = "command line send";
\r
2886 // Send to IP address
\r
2887 CSendingDialog* pdialog = new CSendingDialog(pframeMain, addr, nValue, wtx);
\r
2888 if (!pdialog->ShowModal())
\r
2895 int CMyApp::OnExit()
\r
2898 return wxApp::OnExit();
\r
2901 bool CMyApp::OnExceptionInMainLoop()
\r
2907 catch (std::exception& e)
\r
2909 PrintException(&e, "CMyApp::OnExceptionInMainLoop()");
\r
2910 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2916 PrintException(NULL, "CMyApp::OnExceptionInMainLoop()");
\r
2917 wxLogWarning("Unknown exception");
\r
2925 void CMyApp::OnUnhandledException()
\r
2927 // this shows how we may let some exception propagate uncaught
\r
2932 catch (std::exception& e)
\r
2934 PrintException(&e, "CMyApp::OnUnhandledException()");
\r
2935 wxLogWarning("Exception %s %s", typeid(e).name(), e.what());
\r
2941 PrintException(NULL, "CMyApp::OnUnhandledException()");
\r
2942 wxLogWarning("Unknown exception");
\r
2948 void CMyApp::OnFatalException()
\r
2950 wxMessageBox("Program has crashed and will terminate. ", "Bitcoin", wxOK | wxICON_ERROR);
\r
2958 typedef WINSHELLAPI BOOL (WINAPI *PSHGETSPECIALFOLDERPATHA)(HWND hwndOwner, LPSTR lpszPath, int nFolder, BOOL fCreate);
\r
2960 string MyGetSpecialFolderPath(int nFolder, bool fCreate)
\r
2962 char pszPath[MAX_PATH+100] = "";
\r
2964 // SHGetSpecialFolderPath is not usually available on NT 4.0
\r
2965 HMODULE hShell32 = LoadLibrary("shell32.dll");
\r
2968 PSHGETSPECIALFOLDERPATHA pSHGetSpecialFolderPath =
\r
2969 (PSHGETSPECIALFOLDERPATHA)GetProcAddress(hShell32, "SHGetSpecialFolderPathA");
\r
2970 if (pSHGetSpecialFolderPath)
\r
2971 (*pSHGetSpecialFolderPath)(NULL, pszPath, nFolder, fCreate);
\r
2972 FreeModule(hShell32);
\r
2976 if (pszPath[0] == '\0')
\r
2978 if (nFolder == CSIDL_STARTUP)
\r
2980 strcpy(pszPath, getenv("USERPROFILE"));
\r
2981 strcat(pszPath, "\\Start Menu\\Programs\\Startup");
\r
2983 else if (nFolder == CSIDL_APPDATA)
\r
2985 strcpy(pszPath, getenv("APPDATA"));
\r
2992 string StartupShortcutPath()
\r
2994 return MyGetSpecialFolderPath(CSIDL_STARTUP, true) + "\\Bitcoin.lnk";
\r
2997 bool GetStartOnSystemStartup()
\r
2999 return wxFileExists(StartupShortcutPath());
\r
3002 void SetStartOnSystemStartup(bool fAutoStart)
\r
3004 // If the shortcut exists already, remove it for updating
\r
3005 remove(StartupShortcutPath().c_str());
\r
3009 CoInitialize(NULL);
\r
3011 // Get a pointer to the IShellLink interface.
\r
3012 IShellLink* psl = NULL;
\r
3013 HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL,
\r
3014 CLSCTX_INPROC_SERVER, IID_IShellLink,
\r
3015 reinterpret_cast<void**>(&psl));
\r
3017 if (SUCCEEDED(hres))
\r
3019 // Get the current executable path
\r
3020 char pszExePath[MAX_PATH];
\r
3021 GetModuleFileName(NULL, pszExePath, sizeof(pszExePath));
\r
3023 // Set the path to the shortcut target
\r
3024 psl->SetPath(pszExePath);
\r
3025 PathRemoveFileSpec(pszExePath);
\r
3026 psl->SetWorkingDirectory(pszExePath);
\r
3027 psl->SetShowCmd(SW_SHOWMINNOACTIVE);
\r
3029 // Query IShellLink for the IPersistFile interface for
\r
3030 // saving the shortcut in persistent storage.
\r
3031 IPersistFile* ppf = NULL;
\r
3032 hres = psl->QueryInterface(IID_IPersistFile,
\r
3033 reinterpret_cast<void**>(&ppf));
\r
3034 if (SUCCEEDED(hres))
\r
3036 WCHAR pwsz[MAX_PATH];
\r
3037 // Ensure that the string is ANSI.
\r
3038 MultiByteToWideChar(CP_ACP, 0, StartupShortcutPath().c_str(), -1, pwsz, MAX_PATH);
\r
3039 // Save the link by calling IPersistFile::Save.
\r
3040 hres = ppf->Save(pwsz, TRUE);
\r
3049 bool GetStartOnSystemStartup() { return false; }
\r
3050 void SetStartOnSystemStartup(bool fAutoStart) { }
\r