1 // Copyright (c) 2009-2010 Satoshi Nakamoto
2 // Distributed under the MIT/X11 software license, see the accompanying
3 // file license.txt or http://www.opensource.org/licenses/mit-license.php.
12 DEFINE_EVENT_TYPE(wxEVT_UITHREADCALL)
14 CMainFrame* pframeMain = NULL;
15 CMyTaskBarIcon* ptaskbaricon = NULL;
16 bool fClosedToTray = false;
26 //////////////////////////////////////////////////////////////////////////////
31 void HandleCtrlA(wxKeyEvent& event)
35 wxTextCtrl* textCtrl = (wxTextCtrl*)event.GetEventObject();
36 if (event.GetModifiers() == wxMOD_CONTROL && event.GetKeyCode() == 'A')
37 textCtrl->SetSelection(-1, -1);
42 //char pszHourFormat[256];
43 //pszHourFormat[0] = '\0';
44 //GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ITIME, pszHourFormat, 256);
45 //return (pszHourFormat[0] != '0');
49 string DateStr(int64 nTime)
51 // Can only be used safely here in the UI
52 return (string)wxDateTime((time_t)nTime).FormatDate();
55 string DateTimeStr(int64 nTime)
57 // Can only be used safely here in the UI
58 wxDateTime datetime((time_t)nTime);
60 return (string)datetime.Format("%x %H:%M");
62 return (string)datetime.Format("%x ") + itostr((datetime.GetHour() + 11) % 12 + 1) + (string)datetime.Format(":%M %p");
65 wxString GetItemText(wxListCtrl* listCtrl, int nIndex, int nColumn)
67 // Helper to simplify access to listctrl
69 item.m_itemId = nIndex;
71 item.m_mask = wxLIST_MASK_TEXT;
72 if (!listCtrl->GetItem(item))
74 return item.GetText();
77 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1)
79 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
80 listCtrl->SetItem(nIndex, 1, str1);
84 int InsertLine(wxListCtrl* listCtrl, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
86 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
87 listCtrl->SetItem(nIndex, 1, str1);
88 listCtrl->SetItem(nIndex, 2, str2);
89 listCtrl->SetItem(nIndex, 3, str3);
90 listCtrl->SetItem(nIndex, 4, str4);
94 int InsertLine(wxListCtrl* listCtrl, void* pdata, const wxString& str0, const wxString& str1, const wxString& str2, const wxString& str3, const wxString& str4)
96 int nIndex = listCtrl->InsertItem(listCtrl->GetItemCount(), str0);
97 listCtrl->SetItemPtrData(nIndex, (wxUIntPtr)pdata);
98 listCtrl->SetItem(nIndex, 1, str1);
99 listCtrl->SetItem(nIndex, 2, str2);
100 listCtrl->SetItem(nIndex, 3, str3);
101 listCtrl->SetItem(nIndex, 4, str4);
105 void SetSelection(wxListCtrl* listCtrl, int nIndex)
107 int nSize = listCtrl->GetItemCount();
108 long nState = (wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
109 for (int i = 0; i < nSize; i++)
110 listCtrl->SetItemState(i, (i == nIndex ? nState : 0), nState);
113 int GetSelection(wxListCtrl* listCtrl)
115 int nSize = listCtrl->GetItemCount();
116 for (int i = 0; i < nSize; i++)
117 if (listCtrl->GetItemState(i, wxLIST_STATE_FOCUSED))
122 string HtmlEscape(const char* psz, bool fMultiLine=false)
125 for (const char* p = psz; *p; p++)
127 if (*p == '<') len += 4;
128 else if (*p == '>') len += 4;
129 else if (*p == '&') len += 5;
130 else if (*p == '"') len += 6;
131 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') len += 6;
132 else if (*p == '\n' && fMultiLine) len += 5;
138 for (const char* p = psz; *p; p++)
140 if (*p == '<') str += "<";
141 else if (*p == '>') str += ">";
142 else if (*p == '&') str += "&";
143 else if (*p == '"') str += """;
144 else if (*p == ' ' && p > psz && p[-1] == ' ' && p[1] == ' ') str += " ";
145 else if (*p == '\n' && fMultiLine) str += "<br>\n";
152 string HtmlEscape(const string& str, bool fMultiLine=false)
154 return HtmlEscape(str.c_str(), fMultiLine);
157 void CalledMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y, int* pnRet, bool* pfDone)
159 *pnRet = wxMessageBox(message, caption, style, parent, x, y);
163 int ThreadSafeMessageBox(const string& message, const string& caption, int style, wxWindow* parent, int x, int y)
166 return wxMessageBox(message, caption, style, parent, x, y);
168 if (wxThread::IsMain() || fDaemon)
170 return wxMessageBox(message, caption, style, parent, x, y);
176 UIThreadCall(bind(CalledMessageBox, message, caption, style, parent, x, y, &nRet, &fDone));
184 bool ThreadSafeAskFee(int64 nFeeRequired, const string& strCaption, wxWindow* parent)
186 if (nFeeRequired == 0 || fDaemon)
188 string strMessage = strprintf(
189 _("This transaction is over the size limit. You can still send it for a fee of %s, "
190 "which goes to the nodes that process your transaction and helps to support the network. "
191 "Do you want to pay the fee?"),
192 FormatMoney(nFeeRequired).c_str());
193 return (ThreadSafeMessageBox(strMessage, strCaption, wxYES_NO, parent) == wxYES);
196 void CalledSetStatusBar(const string& strText, int nField)
198 if (pframeMain && pframeMain->m_statusBar)
199 pframeMain->m_statusBar->SetStatusText(strText, nField);
202 void SetDefaultReceivingAddress(const string& strAddress)
204 // Update main window address and database
205 if (pframeMain == NULL)
207 if (strAddress != pframeMain->m_textCtrlAddress->GetValue())
210 if (!AddressToHash160(strAddress, hash160))
212 if (!mapPubKeys.count(hash160))
214 CWalletDB().WriteDefaultKey(mapPubKeys[hash160]);
215 pframeMain->m_textCtrlAddress->SetValue(strAddress);
228 //////////////////////////////////////////////////////////////////////////////
233 CMainFrame::CMainFrame(wxWindow* parent) : CMainFrameBase(parent)
235 Connect(wxEVT_UITHREADCALL, wxCommandEventHandler(CMainFrame::OnUIThreadCall), NULL, this);
237 // Set initially selected page
238 wxNotebookEvent event;
239 event.SetSelection(0);
240 OnNotebookPageChanged(event);
241 m_notebook->ChangeSelection(0);
244 fRefreshListCtrl = false;
245 fRefreshListCtrlRunning = false;
246 fOnSetFocusAddress = false;
248 m_choiceFilter->SetSelection(0);
249 double dResize = 1.0;
251 SetIcon(wxICON(bitcoin));
253 SetIcon(bitcoin80_xpm);
254 SetBackgroundColour(m_toolBar->GetBackgroundColour());
255 wxFont fontTmp = m_staticText41->GetFont();
256 fontTmp.SetFamily(wxFONTFAMILY_TELETYPE);
257 m_staticTextBalance->SetFont(fontTmp);
258 m_staticTextBalance->SetSize(140, 17);
259 // resize to fit ubuntu's huge default font
261 SetSize(dResize * GetSize().GetWidth(), 1.15 * GetSize().GetHeight());
263 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
264 m_listCtrl->SetFocus();
265 ptaskbaricon = new CMyTaskBarIcon();
267 // Mac automatically moves wxID_EXIT, wxID_PREFERENCES and wxID_ABOUT
268 // to their standard places, leaving these menus empty.
269 GetMenuBar()->Remove(2); // remove Help menu
270 GetMenuBar()->Remove(0); // remove File menu
273 // Init column headers
274 int nDateWidth = DateTimeStr(1229413914).size() * 6 + 8;
275 if (!strstr(DateTimeStr(1229413914).c_str(), "2008"))
281 wxListCtrl* pplistCtrl[] = {m_listCtrlAll, m_listCtrlSentReceived, m_listCtrlSent, m_listCtrlReceived};
282 foreach(wxListCtrl* p, pplistCtrl)
284 p->InsertColumn(0, "", wxLIST_FORMAT_LEFT, dResize * 0);
285 p->InsertColumn(1, "", wxLIST_FORMAT_LEFT, dResize * 0);
286 p->InsertColumn(2, _("Status"), wxLIST_FORMAT_LEFT, dResize * 112);
287 p->InsertColumn(3, _("Date"), wxLIST_FORMAT_LEFT, dResize * nDateWidth);
288 p->InsertColumn(4, _("Description"), wxLIST_FORMAT_LEFT, dResize * 409 - nDateWidth);
289 p->InsertColumn(5, _("Debit"), wxLIST_FORMAT_RIGHT, dResize * 79);
290 p->InsertColumn(6, _("Credit"), wxLIST_FORMAT_RIGHT, dResize * 79);
294 int pnWidths[3] = { -100, 88, 300 };
296 pnWidths[1] = pnWidths[1] * 1.1 * dResize;
297 pnWidths[2] = pnWidths[2] * 1.1 * dResize;
299 m_statusBar->SetFieldsCount(3, pnWidths);
301 // Fill your address text box
302 vector<unsigned char> vchPubKey;
303 if (CWalletDB("r").ReadDefaultKey(vchPubKey))
304 m_textCtrlAddress->SetValue(PubKeyToAddress(vchPubKey));
306 // Fill listctrl with wallet transactions
310 CMainFrame::~CMainFrame()
317 void CMainFrame::OnNotebookPageChanged(wxNotebookEvent& event)
320 nPage = event.GetSelection();
323 m_listCtrl = m_listCtrlAll;
324 fShowGenerated = true;
326 fShowReceived = true;
328 else if (nPage == SENTRECEIVED)
330 m_listCtrl = m_listCtrlSentReceived;
331 fShowGenerated = false;
333 fShowReceived = true;
335 else if (nPage == SENT)
337 m_listCtrl = m_listCtrlSent;
338 fShowGenerated = false;
340 fShowReceived = false;
342 else if (nPage == RECEIVED)
344 m_listCtrl = m_listCtrlReceived;
345 fShowGenerated = false;
347 fShowReceived = true;
350 m_listCtrl->SetFocus();
353 void CMainFrame::OnClose(wxCloseEvent& event)
355 if (fMinimizeOnClose && event.CanVeto() && !IsIconized())
357 // Divert close to minimize
359 fClosedToTray = true;
365 CreateThread(Shutdown, NULL);
369 void CMainFrame::OnIconize(wxIconizeEvent& event)
372 // Hide the task bar button when minimized.
373 // Event is sent when the frame is minimized or restored.
374 // wxWidgets 2.8.9 doesn't have IsIconized() so there's no way
375 // to get rid of the deprecated warning. Just ignore it.
376 if (!event.Iconized())
377 fClosedToTray = false;
378 // The tray icon sometimes disappears on ubuntu karmic
379 // Hiding the taskbar button doesn't work cleanly on ubuntu lucid
380 // Reports of CPU peg on 64-bit linux
381 if (fMinimizeToTray && event.Iconized())
382 fClosedToTray = true;
383 Show(!fClosedToTray);
384 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
387 void CMainFrame::OnMouseEvents(wxMouseEvent& event)
391 RAND_add(&event.m_x, sizeof(event.m_x), 0.25);
392 RAND_add(&event.m_y, sizeof(event.m_y), 0.25);
395 void CMainFrame::OnListColBeginDrag(wxListEvent& event)
397 // Hidden columns not resizeable
398 if (event.GetColumn() <= 1 && !fDebug)
404 int CMainFrame::GetSortIndex(const string& strSort)
409 // The wx generic listctrl implementation used on GTK doesn't sort,
410 // so we have to do it ourselves. Remember, we sort in reverse order.
411 // In the wx generic implementation, they store the list of items
412 // in a vector, so indexed lookups are fast, but inserts are slower
413 // the closer they are to the top.
415 int high = m_listCtrl->GetItemCount();
418 int mid = low + ((high - low) / 2);
419 if (strSort.compare(m_listCtrl->GetItemText(mid).c_str()) >= 0)
428 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)
430 strSort = " " + strSort; // leading space to workaround wx2.9.0 ubuntu 9.10 bug
431 long nData = *(long*)&hashKey; // where first char of hidden column is displayed
434 if (!fNew && nIndex == -1)
436 string strHash = " " + hashKey.ToString();
437 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
438 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
442 // fNew is for blind insert, only use if you're sure it's new
443 if (fNew || nIndex == -1)
445 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
449 // If sort key changed, must delete and reinsert to make it relocate
450 if (GetItemText(m_listCtrl, nIndex, 0) != strSort)
452 m_listCtrl->DeleteItem(nIndex);
453 nIndex = m_listCtrl->InsertItem(GetSortIndex(strSort), strSort);
457 m_listCtrl->SetItem(nIndex, 1, " " + hashKey.ToString());
458 m_listCtrl->SetItem(nIndex, 2, str2);
459 m_listCtrl->SetItem(nIndex, 3, str3);
460 m_listCtrl->SetItem(nIndex, 4, str4);
461 m_listCtrl->SetItem(nIndex, 5, str5);
462 m_listCtrl->SetItem(nIndex, 6, str6);
463 m_listCtrl->SetItemData(nIndex, nData);
466 bool CMainFrame::DeleteLine(uint256 hashKey)
468 long nData = *(long*)&hashKey;
472 string strHash = " " + hashKey.ToString();
473 while ((nIndex = m_listCtrl->FindItem(nIndex, nData)) != -1)
474 if (GetItemText(m_listCtrl, nIndex, 1) == strHash)
478 m_listCtrl->DeleteItem(nIndex);
483 string FormatTxStatus(const CWalletTx& wtx)
488 if (wtx.nLockTime < 500000000)
489 return strprintf(_("Open for %d blocks"), nBestHeight - wtx.nLockTime);
491 return strprintf(_("Open until %s"), DateTimeStr(wtx.nLockTime).c_str());
495 int nDepth = wtx.GetDepthInMainChain();
496 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
497 return strprintf(_("%d/offline?"), nDepth);
499 return strprintf(_("%d/unconfirmed"), nDepth);
501 return strprintf(_("%d confirmations"), nDepth);
505 string SingleLine(const string& strIn)
508 bool fOneSpace = false;
509 foreach(int c, strIn)
517 if (fOneSpace && !strOut.empty())
526 bool CMainFrame::InsertTransaction(const CWalletTx& wtx, bool fNew, int nIndex)
528 int64 nTime = wtx.nTimeDisplayed = wtx.GetTxTime();
529 int64 nCredit = wtx.GetCredit(true);
530 int64 nDebit = wtx.GetDebit();
531 int64 nNet = nCredit - nDebit;
532 uint256 hash = wtx.GetHash();
533 string strStatus = FormatTxStatus(wtx);
534 map<string, string> mapValue = wtx.mapValue;
535 wtx.nLinesDisplayed = 1;
539 if (wtx.IsCoinBase())
541 // Don't show generated coin until confirmed by at least one block after it
542 // so we don't get the user's hopes up until it looks like it's probably accepted.
544 // It is not an error when generated blocks are not accepted. By design,
545 // some percentage of blocks, like 10% or more, will end up not accepted.
546 // This is the normal mechanism by which the network copes with latency.
548 // We display regular transactions right away before any confirmation
549 // because they can always get into some block eventually. Generated coins
550 // are special because if their block is not accepted, they are not valid.
552 if (wtx.GetDepthInMainChain() < 2)
554 wtx.nLinesDisplayed = 0;
562 // Find the block the tx is in
563 CBlockIndex* pindex = NULL;
564 map<uint256, CBlockIndex*>::iterator mi = mapBlockIndex.find(wtx.hashBlock);
565 if (mi != mapBlockIndex.end())
566 pindex = (*mi).second;
568 // Sort order, unrecorded transactions sort to the top
569 string strSort = strprintf("%010d-%01d-%010u",
570 (pindex ? pindex->nHeight : INT_MAX),
571 (wtx.IsCoinBase() ? 1 : 0),
575 if (nNet > 0 || wtx.IsCoinBase())
580 string strDescription;
581 if (wtx.IsCoinBase())
584 strDescription = _("Generated");
587 int64 nUnmatured = 0;
588 foreach(const CTxOut& txout, wtx.vout)
589 nUnmatured += txout.GetCredit();
590 if (wtx.IsInMainChain())
592 strDescription = strprintf(_("Generated (%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
594 // Check if the block was requested by anyone
595 if (GetAdjustedTime() - wtx.nTimeReceived > 2 * 60 && wtx.GetRequestCount() == 0)
596 strDescription = _("Generated - Warning: This block was not received by any other nodes and will probably not be accepted!");
600 strDescription = _("Generated (not accepted)");
604 else if (!mapValue["from"].empty() || !mapValue["message"].empty())
606 // Received by IP connection
609 if (!mapValue["from"].empty())
610 strDescription += _("From: ") + mapValue["from"];
611 if (!mapValue["message"].empty())
613 if (!strDescription.empty())
614 strDescription += " - ";
615 strDescription += mapValue["message"];
620 // Received by Bitcoin Address
623 foreach(const CTxOut& txout, wtx.vout)
627 vector<unsigned char> vchPubKey;
628 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
630 CRITICAL_BLOCK(cs_mapAddressBook)
632 //strDescription += _("Received payment to ");
633 //strDescription += _("Received with address ");
634 strDescription += _("From: unknown, Received with: ");
635 string strAddress = PubKeyToAddress(vchPubKey);
636 map<string, string>::iterator mi = mapAddressBook.find(strAddress);
637 if (mi != mapAddressBook.end() && !(*mi).second.empty())
639 string strLabel = (*mi).second;
640 strDescription += strAddress.substr(0,12) + "... ";
641 strDescription += "(" + strLabel + ")";
644 strDescription += strAddress;
652 InsertLine(fNew, nIndex, hash, strSort,
654 nTime ? DateTimeStr(nTime) : "",
655 SingleLine(strDescription),
657 FormatMoney(nNet, true));
661 bool fAllFromMe = true;
662 foreach(const CTxIn& txin, wtx.vin)
663 fAllFromMe = fAllFromMe && txin.IsMine();
665 bool fAllToMe = true;
666 foreach(const CTxOut& txout, wtx.vout)
667 fAllToMe = fAllToMe && txout.IsMine();
669 if (fAllFromMe && fAllToMe)
672 int64 nValue = wtx.vout[0].nValue;
673 InsertLine(fNew, nIndex, hash, strSort,
675 nTime ? DateTimeStr(nTime) : "",
676 _("Payment to yourself"),
679 /// issue: can't tell which is the payment and which is the change anymore
680 // FormatMoney(nNet - nValue, true),
681 // FormatMoney(nValue, true));
691 int64 nTxFee = nDebit - wtx.GetValueOut();
692 wtx.nLinesDisplayed = 0;
693 for (int nOut = 0; nOut < wtx.vout.size(); nOut++)
695 const CTxOut& txout = wtx.vout[nOut];
700 if (!mapValue["to"].empty())
703 strAddress = mapValue["to"];
707 // Sent to Bitcoin Address
709 if (ExtractHash160(txout.scriptPubKey, hash160))
710 strAddress = Hash160ToAddress(hash160);
713 string strDescription = _("To: ");
714 CRITICAL_BLOCK(cs_mapAddressBook)
715 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
716 strDescription += mapAddressBook[strAddress] + " ";
717 strDescription += strAddress;
718 if (!mapValue["message"].empty())
720 if (!strDescription.empty())
721 strDescription += " - ";
722 strDescription += mapValue["message"];
725 int64 nValue = txout.nValue;
732 InsertLine(fNew, nIndex, hash, strprintf("%s-%d", strSort.c_str(), nOut),
734 nTime ? DateTimeStr(nTime) : "",
735 SingleLine(strDescription),
736 FormatMoney(-nValue, true),
738 wtx.nLinesDisplayed++;
744 // Mixed debit transaction, can't break down payees
746 bool fAllMine = true;
747 foreach(const CTxOut& txout, wtx.vout)
748 fAllMine = fAllMine && txout.IsMine();
749 foreach(const CTxIn& txin, wtx.vin)
750 fAllMine = fAllMine && txin.IsMine();
752 InsertLine(fNew, nIndex, hash, strSort,
754 nTime ? DateTimeStr(nTime) : "",
756 FormatMoney(nNet, true),
764 void CMainFrame::RefreshListCtrl()
766 fRefreshListCtrl = true;
770 void CMainFrame::OnIdle(wxIdleEvent& event)
772 if (fRefreshListCtrl)
774 // Collect list of wallet transactions and sort newest first
775 bool fEntered = false;
776 vector<pair<unsigned int, uint256> > vSorted;
777 TRY_CRITICAL_BLOCK(cs_mapWallet)
779 printf("RefreshListCtrl starting\n");
781 fRefreshListCtrl = false;
782 vWalletUpdated.clear();
784 // Do the newest transactions first
785 vSorted.reserve(mapWallet.size());
786 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
788 const CWalletTx& wtx = (*it).second;
789 unsigned int nTime = UINT_MAX - wtx.GetTxTime();
790 vSorted.push_back(make_pair(nTime, (*it).first));
792 m_listCtrl->DeleteAllItems();
797 sort(vSorted.begin(), vSorted.end());
800 for (int i = 0; i < vSorted.size();)
804 bool fEntered = false;
805 TRY_CRITICAL_BLOCK(cs_mapWallet)
808 uint256& hash = vSorted[i++].second;
809 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
810 if (mi != mapWallet.end())
811 InsertTransaction((*mi).second, true);
813 if (!fEntered || i == 100 || i % 500 == 0)
817 printf("RefreshListCtrl done\n");
819 // Update transaction total display
824 // Check for time updates
825 static int64 nLastTime;
826 if (GetTime() > nLastTime + 30)
828 TRY_CRITICAL_BLOCK(cs_mapWallet)
830 nLastTime = GetTime();
831 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
833 CWalletTx& wtx = (*it).second;
834 if (wtx.nTimeDisplayed && wtx.nTimeDisplayed != wtx.GetTxTime())
835 InsertTransaction(wtx, false);
842 void CMainFrame::RefreshStatusColumn()
845 static CBlockIndex* pindexLastBest;
846 static unsigned int nLastRefreshed;
848 int nTop = max((int)m_listCtrl->GetTopItem(), 0);
849 if (nTop == nLastTop && pindexLastBest == pindexBest)
852 TRY_CRITICAL_BLOCK(cs_mapWallet)
855 int nEnd = min(nStart + 100, m_listCtrl->GetItemCount());
857 if (pindexLastBest == pindexBest && nLastRefreshed == nListViewUpdated)
859 // If no updates, only need to do the part that moved onto the screen
860 if (nStart >= nLastTop && nStart < nLastTop + 100)
861 nStart = nLastTop + 100;
862 if (nEnd >= nLastTop && nEnd < nLastTop + 100)
866 pindexLastBest = pindexBest;
867 nLastRefreshed = nListViewUpdated;
869 for (int nIndex = nStart; nIndex < min(nEnd, m_listCtrl->GetItemCount()); nIndex++)
871 uint256 hash((string)GetItemText(m_listCtrl, nIndex, 1));
872 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
873 if (mi == mapWallet.end())
875 printf("CMainFrame::RefreshStatusColumn() : tx not found in mapWallet\n");
878 CWalletTx& wtx = (*mi).second;
879 if (wtx.IsCoinBase() || wtx.GetTxTime() != wtx.nTimeDisplayed)
881 if (!InsertTransaction(wtx, false, nIndex))
882 m_listCtrl->DeleteItem(nIndex--);
885 m_listCtrl->SetItem(nIndex, 2, FormatTxStatus(wtx));
890 void CMainFrame::OnPaint(wxPaintEvent& event)
901 unsigned int nNeedRepaint = 0;
902 unsigned int nLastRepaint = 0;
903 int64 nLastRepaintTime = 0;
904 int64 nRepaintInterval = 500;
906 void ThreadDelayedRepaint(void* parg)
910 if (nLastRepaint != nNeedRepaint && GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
912 nLastRepaint = nNeedRepaint;
915 printf("DelayedRepaint\n");
917 pframeMain->fRefresh = true;
918 pframeMain->GetEventHandler()->AddPendingEvent(event);
921 Sleep(nRepaintInterval);
925 void MainFrameRepaint()
927 // This is called by network code that shouldn't access pframeMain
928 // directly because it could still be running after the UI is closed.
931 // Don't repaint too often
932 static int64 nLastRepaintRequest;
933 if (GetTimeMillis() - nLastRepaintRequest < 100)
938 nLastRepaintRequest = GetTimeMillis();
940 printf("MainFrameRepaint\n");
942 pframeMain->fRefresh = true;
943 pframeMain->GetEventHandler()->AddPendingEvent(event);
947 void CMainFrame::OnPaintListCtrl(wxPaintEvent& event)
949 // Skip lets the listctrl do the paint, we're just hooking the message
953 ptaskbaricon->UpdateTooltip();
958 static int nTransactionCount;
959 bool fPaintedBalance = false;
960 if (GetTimeMillis() - nLastRepaintTime >= nRepaintInterval)
962 nLastRepaint = nNeedRepaint;
963 nLastRepaintTime = GetTimeMillis();
965 // Update listctrl contents
966 if (!vWalletUpdated.empty())
968 TRY_CRITICAL_BLOCK(cs_mapWallet)
971 if (m_listCtrl->GetItemCount())
972 strTop = (string)m_listCtrl->GetItemText(0);
973 foreach(uint256 hash, vWalletUpdated)
975 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
976 if (mi != mapWallet.end())
977 InsertTransaction((*mi).second, false);
979 vWalletUpdated.clear();
980 if (m_listCtrl->GetItemCount() && strTop != (string)m_listCtrl->GetItemText(0))
981 m_listCtrl->ScrollList(0, INT_MIN/2);
986 TRY_CRITICAL_BLOCK(cs_mapWallet)
988 fPaintedBalance = true;
989 m_staticTextBalance->SetLabel(FormatMoney(GetBalance()) + " ");
991 // Count hidden and multi-line transactions
992 nTransactionCount = 0;
993 for (map<uint256, CWalletTx>::iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
995 CWalletTx& wtx = (*it).second;
996 nTransactionCount += wtx.nLinesDisplayed;
1000 if (!vWalletUpdated.empty() || !fPaintedBalance)
1003 // Update status column of visible items only
1004 RefreshStatusColumn();
1006 // Update status bar
1008 if (fGenerateBitcoins)
1009 strGen = _(" Generating");
1010 if (fGenerateBitcoins && vNodes.empty())
1011 strGen = _("(not connected)");
1012 m_statusBar->SetStatusText(strGen, 1);
1014 string strStatus = strprintf(_(" %d connections %d blocks %d transactions"), vNodes.size(), nBestHeight + 1, nTransactionCount);
1015 m_statusBar->SetStatusText(strStatus, 2);
1017 if (fDebug && GetTime() - nThreadSocketHandlerHeartbeat > 60)
1018 m_statusBar->SetStatusText(" ERROR: ThreadSocketHandler has stopped", 0);
1020 // Update receiving address
1021 string strDefaultAddress = PubKeyToAddress(vchDefaultKey);
1022 if (m_textCtrlAddress->GetValue() != strDefaultAddress)
1023 m_textCtrlAddress->SetValue(strDefaultAddress);
1027 void UIThreadCall(boost::function0<void> fn)
1029 // Call this with a function object created with bind.
1030 // bind needs all parameters to match the function's expected types
1031 // and all default parameters specified. Some examples:
1032 // UIThreadCall(bind(wxBell));
1033 // UIThreadCall(bind(wxMessageBox, wxT("Message"), wxT("Title"), wxOK, (wxWindow*)NULL, -1, -1));
1034 // UIThreadCall(bind(&CMainFrame::OnMenuHelpAbout, pframeMain, event));
1037 wxCommandEvent event(wxEVT_UITHREADCALL);
1038 event.SetClientData((void*)new boost::function0<void>(fn));
1039 pframeMain->GetEventHandler()->AddPendingEvent(event);
1043 void CMainFrame::OnUIThreadCall(wxCommandEvent& event)
1045 boost::function0<void>* pfn = (boost::function0<void>*)event.GetClientData();
1050 void CMainFrame::OnMenuFileExit(wxCommandEvent& event)
1056 void CMainFrame::OnMenuOptionsGenerate(wxCommandEvent& event)
1058 // Options->Generate Coins
1059 GenerateBitcoins(event.IsChecked());
1062 void CMainFrame::OnUpdateUIOptionsGenerate(wxUpdateUIEvent& event)
1064 event.Check(fGenerateBitcoins);
1067 void CMainFrame::OnMenuOptionsChangeYourAddress(wxCommandEvent& event)
1069 // Options->Your Receiving Addresses
1070 CAddressBookDialog dialog(this, "", CAddressBookDialog::RECEIVING, false);
1071 if (!dialog.ShowModal())
1075 void CMainFrame::OnMenuOptionsOptions(wxCommandEvent& event)
1078 COptionsDialog dialog(this);
1082 void CMainFrame::OnMenuHelpAbout(wxCommandEvent& event)
1085 CAboutDialog dialog(this);
1089 void CMainFrame::OnButtonSend(wxCommandEvent& event)
1092 CSendDialog dialog(this);
1096 void CMainFrame::OnButtonAddressBook(wxCommandEvent& event)
1098 // Toolbar: Address Book
1099 CAddressBookDialog dialogAddr(this, "", CAddressBookDialog::SENDING, false);
1100 if (dialogAddr.ShowModal() == 2)
1103 CSendDialog dialogSend(this, dialogAddr.GetSelectedAddress());
1104 dialogSend.ShowModal();
1108 void CMainFrame::OnSetFocusAddress(wxFocusEvent& event)
1110 // Automatically select-all when entering window
1112 m_textCtrlAddress->SetSelection(-1, -1);
1113 fOnSetFocusAddress = true;
1116 void CMainFrame::OnMouseEventsAddress(wxMouseEvent& event)
1119 if (fOnSetFocusAddress)
1120 m_textCtrlAddress->SetSelection(-1, -1);
1121 fOnSetFocusAddress = false;
1124 void CMainFrame::OnButtonNew(wxCommandEvent& event)
1127 CGetTextFromUserDialog dialog(this,
1128 _("New Receiving Address"),
1129 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
1131 if (!dialog.ShowModal())
1133 string strName = dialog.GetValue();
1136 string strAddress = PubKeyToAddress(GenerateNewKey());
1139 SetAddressBookName(strAddress, strName);
1140 SetDefaultReceivingAddress(strAddress);
1143 void CMainFrame::OnButtonCopy(wxCommandEvent& event)
1145 // Copy address box to clipboard
1146 if (wxTheClipboard->Open())
1148 wxTheClipboard->SetData(new wxTextDataObject(m_textCtrlAddress->GetValue()));
1149 wxTheClipboard->Close();
1153 void CMainFrame::OnListItemActivated(wxListEvent& event)
1155 uint256 hash((string)GetItemText(m_listCtrl, event.GetIndex(), 1));
1157 CRITICAL_BLOCK(cs_mapWallet)
1159 map<uint256, CWalletTx>::iterator mi = mapWallet.find(hash);
1160 if (mi == mapWallet.end())
1162 printf("CMainFrame::OnListItemActivated() : tx not found in mapWallet\n");
1167 CTxDetailsDialog dialog(this, wtx);
1169 //CTxDetailsDialog* pdialog = new CTxDetailsDialog(this, wtx);
1178 //////////////////////////////////////////////////////////////////////////////
1183 CTxDetailsDialog::CTxDetailsDialog(wxWindow* parent, CWalletTx wtx) : CTxDetailsDialogBase(parent)
1185 CRITICAL_BLOCK(cs_mapAddressBook)
1188 strHTML.reserve(4000);
1189 strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
1191 int64 nTime = wtx.GetTxTime();
1192 int64 nCredit = wtx.GetCredit();
1193 int64 nDebit = wtx.GetDebit();
1194 int64 nNet = nCredit - nDebit;
1198 strHTML += _("<b>Status:</b> ") + FormatTxStatus(wtx);
1199 int nRequests = wtx.GetRequestCount();
1200 if (nRequests != -1)
1203 strHTML += _(", has not been successfully broadcast yet");
1204 else if (nRequests == 1)
1205 strHTML += strprintf(_(", broadcast through %d node"), nRequests);
1207 strHTML += strprintf(_(", broadcast through %d nodes"), nRequests);
1211 strHTML += _("<b>Date:</b> ") + (nTime ? DateTimeStr(nTime) : "") + "<br>";
1217 if (wtx.IsCoinBase())
1219 strHTML += _("<b>Source:</b> Generated<br>");
1221 else if (!wtx.mapValue["from"].empty())
1223 // Online transaction
1224 if (!wtx.mapValue["from"].empty())
1225 strHTML += _("<b>From:</b> ") + HtmlEscape(wtx.mapValue["from"]) + "<br>";
1229 // Offline transaction
1233 foreach(const CTxOut& txout, wtx.vout)
1237 vector<unsigned char> vchPubKey;
1238 if (ExtractPubKey(txout.scriptPubKey, true, vchPubKey))
1240 string strAddress = PubKeyToAddress(vchPubKey);
1241 if (mapAddressBook.count(strAddress))
1243 strHTML += string() + _("<b>From:</b> ") + _("unknown") + "<br>";
1244 strHTML += _("<b>To:</b> ");
1245 strHTML += HtmlEscape(strAddress);
1246 if (!mapAddressBook[strAddress].empty())
1247 strHTML += _(" (yours, label: ") + mapAddressBook[strAddress] + ")";
1249 strHTML += _(" (yours)");
1264 if (!wtx.mapValue["to"].empty())
1266 // Online transaction
1267 strAddress = wtx.mapValue["to"];
1268 strHTML += _("<b>To:</b> ");
1269 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1270 strHTML += mapAddressBook[strAddress] + " ";
1271 strHTML += HtmlEscape(strAddress) + "<br>";
1278 if (wtx.IsCoinBase() && nCredit == 0)
1283 int64 nUnmatured = 0;
1284 foreach(const CTxOut& txout, wtx.vout)
1285 nUnmatured += txout.GetCredit();
1286 strHTML += _("<b>Credit:</b> ");
1287 if (wtx.IsInMainChain())
1288 strHTML += strprintf(_("(%s matures in %d more blocks)"), FormatMoney(nUnmatured).c_str(), wtx.GetBlocksToMaturity());
1290 strHTML += _("(not accepted)");
1298 strHTML += _("<b>Credit:</b> ") + FormatMoney(nNet) + "<br>";
1302 bool fAllFromMe = true;
1303 foreach(const CTxIn& txin, wtx.vin)
1304 fAllFromMe = fAllFromMe && txin.IsMine();
1306 bool fAllToMe = true;
1307 foreach(const CTxOut& txout, wtx.vout)
1308 fAllToMe = fAllToMe && txout.IsMine();
1315 foreach(const CTxOut& txout, wtx.vout)
1320 if (wtx.mapValue["to"].empty())
1322 // Offline transaction
1324 if (ExtractHash160(txout.scriptPubKey, hash160))
1326 string strAddress = Hash160ToAddress(hash160);
1327 strHTML += _("<b>To:</b> ");
1328 if (mapAddressBook.count(strAddress) && !mapAddressBook[strAddress].empty())
1329 strHTML += mapAddressBook[strAddress] + " ";
1330 strHTML += strAddress;
1335 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txout.nValue) + "<br>";
1341 /// issue: can't tell which is the payment and which is the change anymore
1342 //int64 nValue = wtx.vout[0].nValue;
1343 //strHTML += _("<b>Debit:</b> ") + FormatMoney(-nValue) + "<br>";
1344 //strHTML += _("<b>Credit:</b> ") + FormatMoney(nValue) + "<br>";
1347 int64 nTxFee = nDebit - wtx.GetValueOut();
1349 strHTML += _("<b>Transaction fee:</b> ") + FormatMoney(-nTxFee) + "<br>";
1354 // Mixed debit transaction
1356 foreach(const CTxIn& txin, wtx.vin)
1358 strHTML += _("<b>Debit:</b> ") + FormatMoney(-txin.GetDebit()) + "<br>";
1359 foreach(const CTxOut& txout, wtx.vout)
1361 strHTML += _("<b>Credit:</b> ") + FormatMoney(txout.GetCredit()) + "<br>";
1365 strHTML += _("<b>Net amount:</b> ") + FormatMoney(nNet, true) + "<br>";
1371 if (!wtx.mapValue["message"].empty())
1372 strHTML += string() + "<br><b>" + _("Message:") + "</b><br>" + HtmlEscape(wtx.mapValue["message"], true) + "<br>";
1374 if (wtx.IsCoinBase())
1375 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>";
1383 strHTML += "<hr><br>debug print<br><br>";
1384 foreach(const CTxIn& txin, wtx.vin)
1386 strHTML += "<b>Debit:</b> " + FormatMoney(-txin.GetDebit()) + "<br>";
1387 foreach(const CTxOut& txout, wtx.vout)
1389 strHTML += "<b>Credit:</b> " + FormatMoney(txout.GetCredit()) + "<br>";
1391 strHTML += "<b>Inputs:</b><br>";
1392 CRITICAL_BLOCK(cs_mapWallet)
1394 foreach(const CTxIn& txin, wtx.vin)
1396 COutPoint prevout = txin.prevout;
1397 map<uint256, CWalletTx>::iterator mi = mapWallet.find(prevout.hash);
1398 if (mi != mapWallet.end())
1400 const CWalletTx& prev = (*mi).second;
1401 if (prevout.n < prev.vout.size())
1403 strHTML += HtmlEscape(prev.ToString(), true);
1404 strHTML += " " + FormatTxStatus(prev) + ", ";
1405 strHTML = strHTML + "IsMine=" + (prev.vout[prevout.n].IsMine() ? "true" : "false") + "<br>";
1411 strHTML += "<br><hr><br><b>Transaction:</b><br>";
1412 strHTML += HtmlEscape(wtx.ToString(), true);
1417 strHTML += "</font></html>";
1418 string(strHTML.begin(), strHTML.end()).swap(strHTML);
1419 m_htmlWin->SetPage(strHTML);
1420 m_buttonOK->SetFocus();
1424 void CTxDetailsDialog::OnButtonOK(wxCommandEvent& event)
1434 //////////////////////////////////////////////////////////////////////////////
1439 COptionsDialog::COptionsDialog(wxWindow* parent) : COptionsDialogBase(parent)
1441 // Set up list box of page choices
1442 m_listBox->Append(_("Main"));
1443 //m_listBox->Append(_("Test 2"));
1444 m_listBox->SetSelection(0);
1447 m_checkBoxStartOnSystemStartup->SetLabel(_("&Start Bitcoin on window system startup"));
1448 if (!mapArgs.count("-minimizetotray"))
1450 // Minimize to tray is just too buggy on Linux
1451 fMinimizeToTray = false;
1452 m_checkBoxMinimizeToTray->SetValue(false);
1453 m_checkBoxMinimizeToTray->Enable(false);
1454 m_checkBoxMinimizeOnClose->SetLabel(_("&Minimize on close"));
1457 #ifdef __WXMAC_OSX__
1458 m_checkBoxStartOnSystemStartup->Enable(false); // not implemented yet
1462 m_textCtrlTransactionFee->SetValue(FormatMoney(nTransactionFee));
1463 m_checkBoxLimitProcessors->SetValue(fLimitProcessors);
1464 m_spinCtrlLimitProcessors->Enable(fLimitProcessors);
1465 m_spinCtrlLimitProcessors->SetValue(nLimitProcessors);
1466 int nProcessors = wxThread::GetCPUCount();
1467 if (nProcessors < 1)
1469 m_spinCtrlLimitProcessors->SetRange(1, nProcessors);
1470 m_checkBoxStartOnSystemStartup->SetValue(fTmpStartOnSystemStartup = GetStartOnSystemStartup());
1471 m_checkBoxMinimizeToTray->SetValue(fMinimizeToTray);
1472 m_checkBoxMinimizeOnClose->SetValue(fMinimizeOnClose);
1473 m_checkBoxUseProxy->SetValue(fUseProxy);
1474 m_textCtrlProxyIP->Enable(fUseProxy);
1475 m_textCtrlProxyPort->Enable(fUseProxy);
1476 m_staticTextProxyIP->Enable(fUseProxy);
1477 m_staticTextProxyPort->Enable(fUseProxy);
1478 m_textCtrlProxyIP->SetValue(addrProxy.ToStringIP());
1479 m_textCtrlProxyPort->SetValue(addrProxy.ToStringPort());
1481 m_buttonOK->SetFocus();
1484 void COptionsDialog::SelectPage(int nPage)
1486 m_panelMain->Show(nPage == 0);
1487 m_panelTest2->Show(nPage == 1);
1489 m_scrolledWindow->Layout();
1490 m_scrolledWindow->SetScrollbars(0, 0, 0, 0, 0, 0);
1493 void COptionsDialog::OnListBox(wxCommandEvent& event)
1495 SelectPage(event.GetSelection());
1498 void COptionsDialog::OnKillFocusTransactionFee(wxFocusEvent& event)
1501 int64 nTmp = nTransactionFee;
1502 ParseMoney(m_textCtrlTransactionFee->GetValue(), nTmp);
1503 m_textCtrlTransactionFee->SetValue(FormatMoney(nTmp));
1506 void COptionsDialog::OnCheckBoxLimitProcessors(wxCommandEvent& event)
1508 m_spinCtrlLimitProcessors->Enable(event.IsChecked());
1511 void COptionsDialog::OnCheckBoxUseProxy(wxCommandEvent& event)
1513 m_textCtrlProxyIP->Enable(event.IsChecked());
1514 m_textCtrlProxyPort->Enable(event.IsChecked());
1515 m_staticTextProxyIP->Enable(event.IsChecked());
1516 m_staticTextProxyPort->Enable(event.IsChecked());
1519 CAddress COptionsDialog::GetProxyAddr()
1521 // Be careful about byte order, addr.ip and addr.port are big endian
1522 CAddress addr(m_textCtrlProxyIP->GetValue() + ":" + m_textCtrlProxyPort->GetValue());
1523 if (addr.ip == INADDR_NONE)
1524 addr.ip = addrProxy.ip;
1525 int nPort = atoi(m_textCtrlProxyPort->GetValue());
1526 addr.port = htons(nPort);
1527 if (nPort <= 0 || nPort > USHRT_MAX)
1528 addr.port = addrProxy.port;
1532 void COptionsDialog::OnKillFocusProxy(wxFocusEvent& event)
1535 m_textCtrlProxyIP->SetValue(GetProxyAddr().ToStringIP());
1536 m_textCtrlProxyPort->SetValue(GetProxyAddr().ToStringPort());
1540 void COptionsDialog::OnButtonOK(wxCommandEvent& event)
1542 OnButtonApply(event);
1546 void COptionsDialog::OnButtonCancel(wxCommandEvent& event)
1551 void COptionsDialog::OnButtonApply(wxCommandEvent& event)
1555 int64 nPrevTransactionFee = nTransactionFee;
1556 if (ParseMoney(m_textCtrlTransactionFee->GetValue(), nTransactionFee) && nTransactionFee != nPrevTransactionFee)
1557 walletdb.WriteSetting("nTransactionFee", nTransactionFee);
1559 int nPrevMaxProc = (fLimitProcessors ? nLimitProcessors : INT_MAX);
1560 if (fLimitProcessors != m_checkBoxLimitProcessors->GetValue())
1562 fLimitProcessors = m_checkBoxLimitProcessors->GetValue();
1563 walletdb.WriteSetting("fLimitProcessors", fLimitProcessors);
1565 if (nLimitProcessors != m_spinCtrlLimitProcessors->GetValue())
1567 nLimitProcessors = m_spinCtrlLimitProcessors->GetValue();
1568 walletdb.WriteSetting("nLimitProcessors", nLimitProcessors);
1570 if (fGenerateBitcoins && (fLimitProcessors ? nLimitProcessors : INT_MAX) > nPrevMaxProc)
1571 GenerateBitcoins(fGenerateBitcoins);
1573 if (fTmpStartOnSystemStartup != m_checkBoxStartOnSystemStartup->GetValue())
1575 fTmpStartOnSystemStartup = m_checkBoxStartOnSystemStartup->GetValue();
1576 SetStartOnSystemStartup(fTmpStartOnSystemStartup);
1579 if (fMinimizeToTray != m_checkBoxMinimizeToTray->GetValue())
1581 fMinimizeToTray = m_checkBoxMinimizeToTray->GetValue();
1582 walletdb.WriteSetting("fMinimizeToTray", fMinimizeToTray);
1583 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
1586 if (fMinimizeOnClose != m_checkBoxMinimizeOnClose->GetValue())
1588 fMinimizeOnClose = m_checkBoxMinimizeOnClose->GetValue();
1589 walletdb.WriteSetting("fMinimizeOnClose", fMinimizeOnClose);
1592 fUseProxy = m_checkBoxUseProxy->GetValue();
1593 walletdb.WriteSetting("fUseProxy", fUseProxy);
1595 addrProxy = GetProxyAddr();
1596 walletdb.WriteSetting("addrProxy", addrProxy);
1603 //////////////////////////////////////////////////////////////////////////////
1608 CAboutDialog::CAboutDialog(wxWindow* parent) : CAboutDialogBase(parent)
1610 m_staticTextVersion->SetLabel(strprintf(_("version %d.%d.%d beta"), VERSION/10000, (VERSION/100)%100, VERSION%100));
1612 // Change (c) into UTF-8 or ANSI copyright symbol
1613 wxString str = m_staticTextMain->GetLabel();
1615 str.Replace("(c)", wxString::FromUTF8("\xC2\xA9"));
1617 str.Replace("(c)", "\xA9");
1619 m_staticTextMain->SetLabel(str);
1621 // Resize on Linux to make the window fit the text.
1622 // The text was wrapped manually rather than using the Wrap setting because
1623 // the wrap would be too small on Linux and it can't be changed at this point.
1624 wxFont fontTmp = m_staticTextMain->GetFont();
1625 if (fontTmp.GetPointSize() > 8);
1626 fontTmp.SetPointSize(8);
1627 m_staticTextMain->SetFont(fontTmp);
1628 SetSize(GetSize().GetWidth() + 44, GetSize().GetHeight() + 10);
1632 void CAboutDialog::OnButtonOK(wxCommandEvent& event)
1642 //////////////////////////////////////////////////////////////////////////////
1647 CSendDialog::CSendDialog(wxWindow* parent, const wxString& strAddress) : CSendDialogBase(parent)
1650 m_textCtrlAddress->SetValue(strAddress);
1651 m_choiceTransferType->SetSelection(0);
1652 m_bitmapCheckMark->Show(false);
1653 fEnabledPrev = true;
1654 m_textCtrlAddress->SetFocus();
1655 //// todo: should add a display of your balance for convenience
1657 wxFont fontTmp = m_staticTextInstructions->GetFont();
1658 if (fontTmp.GetPointSize() > 9);
1659 fontTmp.SetPointSize(9);
1660 m_staticTextInstructions->SetFont(fontTmp);
1666 iconSend.CopyFromBitmap(wxBitmap(send16noshadow_xpm));
1669 wxCommandEvent event;
1670 OnTextAddress(event);
1672 // Fixup the tab order
1673 m_buttonPaste->MoveAfterInTabOrder(m_buttonCancel);
1674 m_buttonAddress->MoveAfterInTabOrder(m_buttonPaste);
1678 void CSendDialog::OnTextAddress(wxCommandEvent& event)
1682 bool fBitcoinAddress = IsValidBitcoinAddress(m_textCtrlAddress->GetValue());
1683 m_bitmapCheckMark->Show(fBitcoinAddress);
1685 // Grey out message if bitcoin address
1686 bool fEnable = !fBitcoinAddress;
1687 m_staticTextFrom->Enable(fEnable);
1688 m_textCtrlFrom->Enable(fEnable);
1689 m_staticTextMessage->Enable(fEnable);
1690 m_textCtrlMessage->Enable(fEnable);
1691 m_textCtrlMessage->SetBackgroundColour(wxSystemSettings::GetColour(fEnable ? wxSYS_COLOUR_WINDOW : wxSYS_COLOUR_BTNFACE));
1692 if (!fEnable && fEnabledPrev)
1694 strFromSave = m_textCtrlFrom->GetValue();
1695 strMessageSave = m_textCtrlMessage->GetValue();
1696 m_textCtrlFrom->SetValue(_("Will appear as \"From: Unknown\""));
1697 m_textCtrlMessage->SetValue(_("Can't include a message when sending to a Bitcoin address"));
1699 else if (fEnable && !fEnabledPrev)
1701 m_textCtrlFrom->SetValue(strFromSave);
1702 m_textCtrlMessage->SetValue(strMessageSave);
1704 fEnabledPrev = fEnable;
1707 void CSendDialog::OnKillFocusAmount(wxFocusEvent& event)
1709 // Reformat the amount
1711 if (m_textCtrlAmount->GetValue().Trim().empty())
1714 if (ParseMoney(m_textCtrlAmount->GetValue(), nTmp))
1715 m_textCtrlAmount->SetValue(FormatMoney(nTmp));
1718 void CSendDialog::OnButtonAddressBook(wxCommandEvent& event)
1720 // Open address book
1721 CAddressBookDialog dialog(this, m_textCtrlAddress->GetValue(), CAddressBookDialog::SENDING, true);
1722 if (dialog.ShowModal())
1723 m_textCtrlAddress->SetValue(dialog.GetSelectedAddress());
1726 void CSendDialog::OnButtonPaste(wxCommandEvent& event)
1728 // Copy clipboard to address box
1729 if (wxTheClipboard->Open())
1731 if (wxTheClipboard->IsSupported(wxDF_TEXT))
1733 wxTextDataObject data;
1734 wxTheClipboard->GetData(data);
1735 m_textCtrlAddress->SetValue(data.GetText());
1737 wxTheClipboard->Close();
1741 void CSendDialog::OnButtonSend(wxCommandEvent& event)
1744 string strAddress = (string)m_textCtrlAddress->GetValue();
1748 if (!ParseMoney(m_textCtrlAmount->GetValue(), nValue) || nValue <= 0)
1750 wxMessageBox(_("Error in amount "), _("Send Coins"));
1753 if (nValue > GetBalance())
1755 wxMessageBox(_("Amount exceeds your balance "), _("Send Coins"));
1758 if (nValue + nTransactionFee > GetBalance())
1760 wxMessageBox(string(_("Total exceeds your balance when the ")) + FormatMoney(nTransactionFee) + _(" transaction fee is included "), _("Send Coins"));
1764 // Parse bitcoin address
1766 bool fBitcoinAddress = AddressToHash160(strAddress, hash160);
1768 if (fBitcoinAddress)
1770 // Send to bitcoin address
1771 CScript scriptPubKey;
1772 scriptPubKey << OP_DUP << OP_HASH160 << hash160 << OP_EQUALVERIFY << OP_CHECKSIG;
1774 string strError = SendMoney(scriptPubKey, nValue, wtx, true);
1776 wxMessageBox(_("Payment sent "), _("Sending..."));
1777 else if (strError != "ABORTED")
1778 wxMessageBox(strError + " ", _("Sending..."));
1783 CAddress addr(strAddress);
1784 if (!addr.IsValid())
1786 wxMessageBox(_("Invalid address "), _("Send Coins"));
1791 wtx.mapValue["to"] = strAddress;
1792 wtx.mapValue["from"] = m_textCtrlFrom->GetValue();
1793 wtx.mapValue["message"] = m_textCtrlMessage->GetValue();
1795 // Send to IP address
1796 CSendingDialog* pdialog = new CSendingDialog(this, addr, nValue, wtx);
1797 if (!pdialog->ShowModal())
1801 CRITICAL_BLOCK(cs_mapAddressBook)
1802 if (!mapAddressBook.count(strAddress))
1803 SetAddressBookName(strAddress, "");
1808 void CSendDialog::OnButtonCancel(wxCommandEvent& event)
1819 //////////////////////////////////////////////////////////////////////////////
1824 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
1829 start = wxDateTime::UNow();
1830 memset(pszStatus, 0, sizeof(pszStatus));
1837 SetSize(1.2 * GetSize().GetWidth(), 1.08 * GetSize().GetHeight());
1840 SetTitle(strprintf(_("Sending %s to %s"), FormatMoney(nPrice).c_str(), wtx.mapValue["to"].c_str()));
1841 m_textCtrlStatus->SetValue("");
1843 CreateThread(SendingDialogStartTransfer, this);
1846 CSendingDialog::~CSendingDialog()
1848 printf("~CSendingDialog()\n");
1851 void CSendingDialog::Close()
1853 // Last one out turn out the lights.
1854 // fWorkDone signals that work side is done and UI thread should call destroy.
1855 // fUIDone signals that UI window has closed and work thread should call destroy.
1856 // This allows the window to disappear and end modality when cancelled
1857 // without making the user wait for ConnectNode to return. The dialog object
1858 // hangs around in the background until the work thread exits.
1869 void CSendingDialog::OnClose(wxCloseEvent& event)
1871 if (!event.CanVeto() || fWorkDone || fAbort || !fCanCancel)
1878 wxCommandEvent cmdevent;
1879 OnButtonCancel(cmdevent);
1883 void CSendingDialog::OnButtonOK(wxCommandEvent& event)
1889 void CSendingDialog::OnButtonCancel(wxCommandEvent& event)
1895 void CSendingDialog::OnPaint(wxPaintEvent& event)
1898 if (strlen(pszStatus) > 130)
1899 m_textCtrlStatus->SetValue(string("\n") + pszStatus);
1901 m_textCtrlStatus->SetValue(string("\n\n") + pszStatus);
1902 m_staticTextSending->SetFocus();
1904 m_buttonCancel->Enable(false);
1907 m_buttonOK->Enable(true);
1908 m_buttonOK->SetFocus();
1909 m_buttonCancel->Enable(false);
1911 if (fAbort && fCanCancel && IsShown())
1913 strcpy(pszStatus, _("CANCELLED"));
1914 m_buttonOK->Enable(true);
1915 m_buttonOK->SetFocus();
1916 m_buttonCancel->Enable(false);
1917 m_buttonCancel->SetLabel(_("Cancelled"));
1919 wxMessageBox(_("Transfer cancelled "), _("Sending..."), wxOK, this);
1925 // Everything from here on is not in the UI thread and must only communicate
1926 // with the rest of the dialog through variables and calling repaint.
1929 void CSendingDialog::Repaint()
1933 GetEventHandler()->AddPendingEvent(event);
1936 bool CSendingDialog::Status()
1943 if (fAbort && fCanCancel)
1945 memset(pszStatus, 0, 10);
1946 strcpy(pszStatus, _("CANCELLED"));
1954 bool CSendingDialog::Status(const string& str)
1959 // This can be read by the UI thread at any time,
1960 // so copy in a way that can be read cleanly at all times.
1961 memset(pszStatus, 0, min(str.size()+1, sizeof(pszStatus)));
1962 strlcpy(pszStatus, str.c_str(), sizeof(pszStatus));
1968 bool CSendingDialog::Error(const string& str)
1972 Status(string(_("Error: ")) + str);
1976 void SendingDialogStartTransfer(void* parg)
1978 ((CSendingDialog*)parg)->StartTransfer();
1981 void CSendingDialog::StartTransfer()
1983 // Make sure we have enough money
1984 if (nPrice + nTransactionFee > GetBalance())
1986 Error(_("Insufficient funds"));
1990 // We may have connected already for product details
1991 if (!Status(_("Connecting...")))
1993 CNode* pnode = ConnectNode(addr, 15 * 60);
1996 Error(_("Unable to connect"));
2000 // Send order to seller, with response going to OnReply2 via event handler
2001 if (!Status(_("Requesting public key...")))
2003 pnode->PushRequest("checkorder", wtx, SendingDialogOnReply2, this);
2006 void SendingDialogOnReply2(void* parg, CDataStream& vRecv)
2008 ((CSendingDialog*)parg)->OnReply2(vRecv);
2011 void CSendingDialog::OnReply2(CDataStream& vRecv)
2013 if (!Status(_("Received public key...")))
2016 CScript scriptPubKey;
2024 vRecv >> strMessage;
2025 Error(_("Transfer was not accepted"));
2026 //// todo: enlarge the window and enable a hidden white box to put seller's message
2029 vRecv >> scriptPubKey;
2033 //// what do we want to do about this?
2034 Error(_("Invalid response received"));
2038 // Pause to give the user a chance to cancel
2039 while (wxDateTime::UNow() < start + wxTimeSpan(0, 0, 0, 2 * 1000))
2046 CRITICAL_BLOCK(cs_main)
2049 if (!Status(_("Creating transaction...")))
2051 if (nPrice + nTransactionFee > GetBalance())
2053 Error(_("Insufficient funds"));
2058 if (!CreateTransaction(scriptPubKey, nPrice, wtx, key, nFeeRequired))
2060 if (nPrice + nFeeRequired > GetBalance())
2061 Error(strprintf(_("This is an oversized transaction that requires a transaction fee of %s"), FormatMoney(nFeeRequired).c_str()));
2063 Error(_("Transaction creation failed"));
2068 if (!ThreadSafeAskFee(nFeeRequired, _("Sending..."), this))
2070 Error(_("Transaction aborted"));
2074 // Make sure we're still connected
2075 CNode* pnode = ConnectNode(addr, 2 * 60 * 60);
2078 Error(_("Lost connection, transaction cancelled"));
2082 // Last chance to cancel
2094 if (!Status(_("Sending payment...")))
2098 if (!CommitTransaction(wtx, key))
2100 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."));
2104 // Send payment tx to seller, with response going to OnReply3 via event handler
2105 CWalletTx wtxSend = wtx;
2106 wtxSend.fFromMe = false;
2107 pnode->PushRequest("submitorder", wtxSend, SendingDialogOnReply3, this);
2109 Status(_("Waiting for confirmation..."));
2114 void SendingDialogOnReply3(void* parg, CDataStream& vRecv)
2116 ((CSendingDialog*)parg)->OnReply3(vRecv);
2119 void CSendingDialog::OnReply3(CDataStream& vRecv)
2127 Error(_("The payment was sent, but the recipient was unable to verify it.\n"
2128 "The transaction is recorded and will credit to the recipient,\n"
2129 "but the comment information will be blank."));
2135 //// what do we want to do about this?
2136 Error(_("Payment was sent, but an invalid response was received"));
2142 Status(_("Payment completed"));
2150 //////////////////////////////////////////////////////////////////////////////
2152 // CAddressBookDialog
2155 CAddressBookDialog::CAddressBookDialog(wxWindow* parent, const wxString& strInitSelected, int nPageIn, bool fDuringSendIn) : CAddressBookDialogBase(parent)
2157 // Set initially selected page
2158 wxNotebookEvent event;
2159 event.SetSelection(nPageIn);
2160 OnNotebookPageChanged(event);
2161 m_notebook->ChangeSelection(nPageIn);
2163 fDuringSend = fDuringSendIn;
2165 m_buttonCancel->Show(false);
2168 wxIcon iconAddressBook;
2169 iconAddressBook.CopyFromBitmap(wxBitmap(addressbook16_xpm));
2170 SetIcon(iconAddressBook);
2172 // Init column headers
2173 m_listCtrlSending->InsertColumn(0, _("Name"), wxLIST_FORMAT_LEFT, 200);
2174 m_listCtrlSending->InsertColumn(1, _("Address"), wxLIST_FORMAT_LEFT, 350);
2175 m_listCtrlSending->SetFocus();
2176 m_listCtrlReceiving->InsertColumn(0, _("Label"), wxLIST_FORMAT_LEFT, 200);
2177 m_listCtrlReceiving->InsertColumn(1, _("Bitcoin Address"), wxLIST_FORMAT_LEFT, 350);
2178 m_listCtrlReceiving->SetFocus();
2180 // Fill listctrl with address book data
2181 CRITICAL_BLOCK(cs_mapKeys)
2182 CRITICAL_BLOCK(cs_mapAddressBook)
2184 string strDefaultReceiving = (string)pframeMain->m_textCtrlAddress->GetValue();
2185 foreach(const PAIRTYPE(string, string)& item, mapAddressBook)
2187 string strAddress = item.first;
2188 string strName = item.second;
2190 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2191 wxListCtrl* plistCtrl = fMine ? m_listCtrlReceiving : m_listCtrlSending;
2192 int nIndex = InsertLine(plistCtrl, strName, strAddress);
2193 if (strAddress == (fMine ? strDefaultReceiving : string(strInitSelected)))
2194 plistCtrl->SetItemState(nIndex, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED, wxLIST_STATE_SELECTED|wxLIST_STATE_FOCUSED);
2199 wxString CAddressBookDialog::GetSelectedAddress()
2201 int nIndex = GetSelection(m_listCtrl);
2204 return GetItemText(m_listCtrl, nIndex, 1);
2207 wxString CAddressBookDialog::GetSelectedSendingAddress()
2209 int nIndex = GetSelection(m_listCtrlSending);
2212 return GetItemText(m_listCtrlSending, nIndex, 1);
2215 wxString CAddressBookDialog::GetSelectedReceivingAddress()
2217 int nIndex = GetSelection(m_listCtrlReceiving);
2220 return GetItemText(m_listCtrlReceiving, nIndex, 1);
2223 void CAddressBookDialog::OnNotebookPageChanged(wxNotebookEvent& event)
2226 nPage = event.GetSelection();
2227 if (nPage == SENDING)
2228 m_listCtrl = m_listCtrlSending;
2229 else if (nPage == RECEIVING)
2230 m_listCtrl = m_listCtrlReceiving;
2231 m_buttonDelete->Show(nPage == SENDING);
2232 m_buttonCopy->Show(nPage == RECEIVING);
2234 m_listCtrl->SetFocus();
2237 void CAddressBookDialog::OnListEndLabelEdit(wxListEvent& event)
2239 // Update address book with edited name
2241 if (event.IsEditCancelled())
2243 string strAddress = (string)GetItemText(m_listCtrl, event.GetIndex(), 1);
2244 SetAddressBookName(strAddress, string(event.GetText()));
2245 pframeMain->RefreshListCtrl();
2248 void CAddressBookDialog::OnListItemSelected(wxListEvent& event)
2251 if (nPage == RECEIVING)
2252 SetDefaultReceivingAddress((string)GetSelectedReceivingAddress());
2255 void CAddressBookDialog::OnListItemActivated(wxListEvent& event)
2260 // Doubleclick returns selection
2261 EndModal(GetSelectedAddress() != "" ? 2 : 0);
2265 // Doubleclick edits item
2266 wxCommandEvent event2;
2267 OnButtonEdit(event2);
2270 void CAddressBookDialog::OnButtonDelete(wxCommandEvent& event)
2272 if (nPage != SENDING)
2274 for (int nIndex = m_listCtrl->GetItemCount()-1; nIndex >= 0; nIndex--)
2276 if (m_listCtrl->GetItemState(nIndex, wxLIST_STATE_SELECTED))
2278 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2279 CWalletDB().EraseName(strAddress);
2280 m_listCtrl->DeleteItem(nIndex);
2283 pframeMain->RefreshListCtrl();
2286 void CAddressBookDialog::OnButtonCopy(wxCommandEvent& event)
2288 // Copy address box to clipboard
2289 if (wxTheClipboard->Open())
2291 wxTheClipboard->SetData(new wxTextDataObject(GetSelectedAddress()));
2292 wxTheClipboard->Close();
2296 bool CAddressBookDialog::CheckIfMine(const string& strAddress, const string& strTitle)
2299 bool fMine = (AddressToHash160(strAddress, hash160) && mapPubKeys.count(hash160));
2301 wxMessageBox(_("This is one of your own addresses for receiving payments and cannot be entered in the address book. "), strTitle);
2305 void CAddressBookDialog::OnButtonEdit(wxCommandEvent& event)
2307 int nIndex = GetSelection(m_listCtrl);
2310 string strName = (string)m_listCtrl->GetItemText(nIndex);
2311 string strAddress = (string)GetItemText(m_listCtrl, nIndex, 1);
2312 string strAddressOrg = strAddress;
2314 if (nPage == SENDING)
2316 // Ask name and address
2319 CGetTextFromUserDialog dialog(this, _("Edit Address"), _("Name"), strName, _("Address"), strAddress);
2320 if (!dialog.ShowModal())
2322 strName = dialog.GetValue1();
2323 strAddress = dialog.GetValue2();
2325 while (CheckIfMine(strAddress, _("Edit Address")));
2328 else if (nPage == RECEIVING)
2331 CGetTextFromUserDialog dialog(this, _("Edit Address Label"), _("Label"), strName);
2332 if (!dialog.ShowModal())
2334 strName = dialog.GetValue();
2338 if (strAddress != strAddressOrg)
2339 CWalletDB().EraseName(strAddressOrg);
2340 SetAddressBookName(strAddress, strName);
2341 m_listCtrl->SetItem(nIndex, 1, strAddress);
2342 m_listCtrl->SetItemText(nIndex, strName);
2343 pframeMain->RefreshListCtrl();
2346 void CAddressBookDialog::OnButtonNew(wxCommandEvent& event)
2351 if (nPage == SENDING)
2353 // Ask name and address
2356 CGetTextFromUserDialog dialog(this, _("Add Address"), _("Name"), strName, _("Address"), strAddress);
2357 if (!dialog.ShowModal())
2359 strName = dialog.GetValue1();
2360 strAddress = dialog.GetValue2();
2362 while (CheckIfMine(strAddress, _("Add Address")));
2364 else if (nPage == RECEIVING)
2367 CGetTextFromUserDialog dialog(this,
2368 _("New Receiving Address"),
2369 _("It's good policy to use a new address for each payment you receive.\n\nLabel"),
2371 if (!dialog.ShowModal())
2373 strName = dialog.GetValue();
2376 strAddress = PubKeyToAddress(GenerateNewKey());
2379 // Add to list and select it
2380 SetAddressBookName(strAddress, strName);
2381 int nIndex = InsertLine(m_listCtrl, strName, strAddress);
2382 SetSelection(m_listCtrl, nIndex);
2383 m_listCtrl->SetFocus();
2384 if (nPage == SENDING)
2385 pframeMain->RefreshListCtrl();
2388 void CAddressBookDialog::OnButtonOK(wxCommandEvent& event)
2391 EndModal(GetSelectedAddress() != "" ? 1 : 0);
2394 void CAddressBookDialog::OnButtonCancel(wxCommandEvent& event)
2400 void CAddressBookDialog::OnClose(wxCloseEvent& event)
2411 //////////////////////////////////////////////////////////////////////////////
2418 ID_TASKBAR_RESTORE = 10001,
2420 ID_TASKBAR_GENERATE,
2424 BEGIN_EVENT_TABLE(CMyTaskBarIcon, wxTaskBarIcon)
2425 EVT_TASKBAR_LEFT_DCLICK(CMyTaskBarIcon::OnLeftButtonDClick)
2426 EVT_MENU(ID_TASKBAR_RESTORE, CMyTaskBarIcon::OnMenuRestore)
2427 EVT_MENU(ID_TASKBAR_OPTIONS, CMyTaskBarIcon::OnMenuOptions)
2428 EVT_MENU(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnMenuGenerate)
2429 EVT_UPDATE_UI(ID_TASKBAR_GENERATE, CMyTaskBarIcon::OnUpdateUIGenerate)
2430 EVT_MENU(ID_TASKBAR_EXIT, CMyTaskBarIcon::OnMenuExit)
2433 void CMyTaskBarIcon::Show(bool fShow)
2435 static char pszPrevTip[200];
2438 string strTooltip = _("Bitcoin");
2439 if (fGenerateBitcoins)
2440 strTooltip = _("Bitcoin - Generating");
2441 if (fGenerateBitcoins && vNodes.empty())
2442 strTooltip = _("Bitcoin - (not connected)");
2444 // Optimization, only update when changed, using char array to be reentrant
2445 if (strncmp(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip)-1) != 0)
2447 strlcpy(pszPrevTip, strTooltip.c_str(), sizeof(pszPrevTip));
2449 // somehow it'll choose the wrong size and scale it down if
2450 // we use the main icon, so we hand it one with only 16x16
2451 SetIcon(wxICON(favicon), strTooltip);
2453 SetIcon(bitcoin80_xpm, strTooltip);
2459 strlcpy(pszPrevTip, "", sizeof(pszPrevTip));
2464 void CMyTaskBarIcon::Hide()
2469 void CMyTaskBarIcon::OnLeftButtonDClick(wxTaskBarIconEvent& event)
2474 void CMyTaskBarIcon::OnMenuRestore(wxCommandEvent& event)
2479 void CMyTaskBarIcon::OnMenuOptions(wxCommandEvent& event)
2481 // Since it's modal, get the main window to do it
2482 wxCommandEvent event2(wxEVT_COMMAND_MENU_SELECTED, wxID_PREFERENCES);
2483 pframeMain->GetEventHandler()->AddPendingEvent(event2);
2486 void CMyTaskBarIcon::Restore()
2489 wxIconizeEvent event(0, false);
2490 pframeMain->GetEventHandler()->AddPendingEvent(event);
2491 pframeMain->Iconize(false);
2492 pframeMain->Raise();
2495 void CMyTaskBarIcon::OnMenuGenerate(wxCommandEvent& event)
2497 GenerateBitcoins(event.IsChecked());
2500 void CMyTaskBarIcon::OnUpdateUIGenerate(wxUpdateUIEvent& event)
2502 event.Check(fGenerateBitcoins);
2505 void CMyTaskBarIcon::OnMenuExit(wxCommandEvent& event)
2507 pframeMain->Close(true);
2510 void CMyTaskBarIcon::UpdateTooltip()
2512 if (IsIconInstalled())
2516 wxMenu* CMyTaskBarIcon::CreatePopupMenu()
2518 wxMenu* pmenu = new wxMenu;
2519 pmenu->Append(ID_TASKBAR_RESTORE, _("&Open Bitcoin"));
2520 pmenu->Append(ID_TASKBAR_OPTIONS, _("O&ptions..."));
2521 pmenu->AppendCheckItem(ID_TASKBAR_GENERATE, _("&Generate Coins"))->Check(fGenerateBitcoins);
2522 #ifndef __WXMAC_OSX__ // Mac has built-in quit menu
2523 pmenu->AppendSeparator();
2524 pmenu->Append(ID_TASKBAR_EXIT, _("E&xit"));
2541 void CreateMainWindow()
2543 pframeMain = new CMainFrame(NULL);
2544 if (mapArgs.count("-min"))
2545 pframeMain->Iconize(true);
2547 if (!mapArgs.count("-minimizetotray"))
2548 fMinimizeToTray = false;
2550 pframeMain->Show(true); // have to show first to get taskbar button to hide
2551 if (fMinimizeToTray && pframeMain->IsIconized())
2552 fClosedToTray = true;
2553 pframeMain->Show(!fClosedToTray);
2554 ptaskbaricon->Show(fMinimizeToTray || fClosedToTray);
2555 CreateThread(ThreadDelayedRepaint, NULL);