dropped unused TSVN settings
[TortoiseGit.git] / src / TortoiseProc / ProgressDlg.cpp
blob4dd3a312e1923c38895c995ef215b2595c211575
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2011 - TortoiseGit
5 // This program is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU General Public License
7 // as published by the Free Software Foundation; either version 2
8 // of the License, or (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software Foundation,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 // ProgressDlg.cpp : implementation file
22 #include "stdafx.h"
23 #include "TortoiseProc.h"
24 #include "ProgressDlg.h"
25 #include "Git.h"
26 #include "atlconv.h"
27 #include "UnicodeUtils.h"
28 #include "IconMenu.h"
29 #include "CommonResource.h"
30 #include "Tlhelp32.h"
31 #include "AppUtils.h"
33 // CProgressDlg dialog
35 IMPLEMENT_DYNAMIC(CProgressDlg, CResizableStandAloneDialog)
37 CProgressDlg::CProgressDlg(CWnd* pParent /*=NULL*/)
38 : CResizableStandAloneDialog(CProgressDlg::IDD, pParent), m_bShowCommand(true), m_bAutoCloseOnSuccess(false), m_bAbort(false), m_bDone(false)
40 m_pThread = NULL;
41 m_bAltAbortPress=false;
42 m_bBufferAll=false;
45 CProgressDlg::~CProgressDlg()
47 if(m_pThread != NULL)
49 delete m_pThread;
53 void CProgressDlg::DoDataExchange(CDataExchange* pDX)
55 CDialog::DoDataExchange(pDX);
56 DDX_Control(pDX, IDC_CURRENT, this->m_CurrentWork);
57 DDX_Control(pDX, IDC_TITLE_ANIMATE, this->m_Animate);
58 DDX_Control(pDX, IDC_RUN_PROGRESS, this->m_Progress);
59 DDX_Control(pDX, IDC_LOG, this->m_Log);
60 DDX_Control(pDX, IDC_PROGRESS_BUTTON1, this->m_ctrlPostCmd);
63 BEGIN_MESSAGE_MAP(CProgressDlg, CResizableStandAloneDialog)
64 ON_WM_CLOSE()
65 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
66 ON_BN_CLICKED(IDOK, &CProgressDlg::OnBnClickedOk)
67 ON_BN_CLICKED(IDC_PROGRESS_BUTTON1,&CProgressDlg::OnBnClickedButton1)
68 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
69 END_MESSAGE_MAP()
71 BOOL CProgressDlg::OnInitDialog()
73 CResizableStandAloneDialog::OnInitDialog();
75 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
76 // do this, Explorer would be unable to send that message to our window if we
77 // were running elevated. It's OK to make the call all the time, since if we're
78 // not elevated, this is a no-op.
79 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
80 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
81 HMODULE hUser = ::LoadLibrary(_T("user32.dll"));
82 if (hUser)
84 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
85 if (pfnChangeWindowMessageFilterEx)
87 pfnChangeWindowMessageFilterEx(m_hWnd, WM_TASKBARBTNCREATED, MSGFLT_ALLOW, &cfs);
89 FreeLibrary(hUser);
91 m_pTaskbarList.Release();
92 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
94 AddAnchor(IDC_TITLE_ANIMATE, TOP_LEFT, TOP_RIGHT);
95 AddAnchor(IDC_RUN_PROGRESS, TOP_LEFT,TOP_RIGHT);
96 AddAnchor(IDC_LOG, TOP_LEFT,BOTTOM_RIGHT);
98 AddAnchor(IDOK,BOTTOM_RIGHT);
99 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
100 AddAnchor(IDC_PROGRESS_BUTTON1,BOTTOM_LEFT);
101 AddAnchor(IDC_CURRENT,TOP_LEFT);
103 this->GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_HIDE);
104 m_Animate.Open(IDR_DOWNLOAD);
106 CString InitialText;
107 if ( !m_PreText.IsEmpty() )
109 InitialText = m_PreText + _T("\r\n");
111 #if 0
112 if (m_bShowCommand && (!m_GitCmd.IsEmpty() ))
114 InitialText += m_GitCmd+_T("\r\n\r\n");
116 #endif
117 m_Log.SetWindowTextW(InitialText);
118 m_CurrentWork.SetWindowTextW(_T(""));
120 EnableSaveRestore(_T("ProgressDlg"));
122 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
123 if (m_pThread==NULL)
125 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
127 else
129 m_pThread->m_bAutoDelete = FALSE;
130 m_pThread->ResumeThread();
133 if(!m_Title.IsEmpty())
134 this->SetWindowText(m_Title);
136 CString sWindowTitle;
137 GetWindowText(sWindowTitle);
138 CAppUtils::SetWindowTitle(m_hWnd, g_Git.m_CurrentDir, sWindowTitle);
140 if(m_PostCmdList.GetCount()>0)
141 m_ctrlPostCmd.AddEntries(m_PostCmdList);
143 return TRUE;
146 UINT CProgressDlg::ProgressThreadEntry(LPVOID pVoid)
148 return ((CProgressDlg*)pVoid)->ProgressThread();
151 //static function, Share with SyncDialog
152 UINT CProgressDlg::RunCmdList(CWnd *pWnd,std::vector<CString> &cmdlist,bool bShowCommand,CString *pfilename,bool *bAbort,CGitByteArray *pdata)
154 UINT ret=0;
156 PROCESS_INFORMATION pi;
157 HANDLE hRead = 0;
159 memset(&pi,0,sizeof(PROCESS_INFORMATION));
161 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_START,0);
163 if(pdata)
164 pdata->clear();
166 for(int i=0;i<cmdlist.size();i++)
168 if(cmdlist[i].IsEmpty())
169 continue;
171 if (bShowCommand)
173 CStringA str(cmdlist[i].Trim()+_T("\n\n"));
174 for(int j=0;j<str.GetLength();j++)
176 if(pdata)
178 pdata->m_critSec.Lock();
179 pdata->push_back(str[j]);
180 pdata->m_critSec.Unlock();
182 else
183 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,str[j]);
185 if(pdata)
186 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
189 g_Git.RunAsync(cmdlist[i].Trim(),&pi, &hRead, NULL, pfilename);
191 DWORD readnumber;
192 char byte;
193 CString output;
194 while(ReadFile(hRead,&byte,1,&readnumber,NULL))
196 if(pdata)
198 if(byte == 0)
199 byte = '\n';
201 pdata->m_critSec.Lock();
202 pdata->push_back( byte);
203 pdata->m_critSec.Unlock();
205 if(byte == '\r' || byte == '\n')
206 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
208 else
209 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,byte);
212 CloseHandle(pi.hThread);
214 WaitForSingleObject(pi.hProcess, INFINITE);
216 DWORD status=0;
217 if(!GetExitCodeProcess(pi.hProcess,&status) || *bAbort)
219 CloseHandle(pi.hProcess);
221 CloseHandle(hRead);
223 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_FAILED,0);
224 return TGIT_GIT_ERROR_GET_EXIT_CODE;
226 ret |= status;
229 CloseHandle(pi.hProcess);
231 CloseHandle(hRead);
233 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_END,0);
235 return ret;
239 UINT CProgressDlg::ProgressThread()
242 m_GitCmdList.push_back(m_GitCmd);
244 CString *pfilename;
246 if(m_LogFile.IsEmpty())
247 pfilename=NULL;
248 else
249 pfilename=&m_LogFile;
251 m_GitStatus = RunCmdList(this,m_GitCmdList,m_bShowCommand,pfilename,&m_bAbort,&this->m_Databuf);;
252 return 0;
255 LRESULT CProgressDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
257 if(wParam == MSG_PROGRESSDLG_START)
259 m_BufStart = 0 ;
260 m_Animate.Play(0,-1,-1);
261 this->DialogEnableWindow(IDOK,FALSE);
262 if (m_pTaskbarList)
264 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
265 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
268 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
270 if(m_bBufferAll)
272 m_Databuf.m_critSec.Lock();
273 m_Databuf.push_back(0);
274 m_Databuf.m_critSec.Unlock();
275 InsertCRLF();
276 m_Databuf.m_critSec.Lock();
277 m_Log.SetWindowText(Convert2UnionCode((char*)&m_Databuf[0]));
278 m_Databuf.m_critSec.Unlock();
279 m_Log.LineScroll(m_Log.GetLineCount() - m_Log.GetFirstVisibleLine() - 4);
281 m_BufStart=0;
282 m_Databuf.m_critSec.Lock();
283 this->m_Databuf.clear();
284 m_Databuf.m_critSec.Unlock();
286 m_bDone = true;
287 m_Animate.Stop();
288 m_Progress.SetPos(100);
289 this->DialogEnableWindow(IDOK,TRUE);
291 CString err;
292 err.Format(_T("\r\nFailed 0x%x (git returned a wrong return code at some time)\r\n"),m_GitStatus);
293 if(this->m_GitStatus)
295 if (m_pTaskbarList)
297 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
298 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
300 //InsertColorText(this->m_Log,err,RGB(255,0,0));
302 else {
303 if (m_pTaskbarList)
304 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
305 InsertColorText(this->m_Log,_T("\r\nSuccess\r\n"),RGB(0,0,255));
306 this->DialogEnableWindow(IDCANCEL,FALSE);
309 if(wParam == MSG_PROGRESSDLG_END && m_GitStatus == 0)
311 if(m_bAutoCloseOnSuccess)
312 EndDialog(IDOK);
314 if(m_PostCmdList.GetCount() > 0)
316 //GetDlgItem(IDC_PROGRESS_BUTTON1)->SetWindowText(m_changeAbortButtonOnSuccessTo);
317 GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_SHOW);
318 //GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);
319 //Set default button is "close" rather than "push"
320 this->SendMessage(WM_NEXTDLGCTL, (WPARAM)GetDlgItem(IDOK)->m_hWnd, TRUE);
325 if(!m_bBufferAll)
327 if(lParam == 0)
329 m_Databuf.m_critSec.Lock();
330 for(int i=this->m_BufStart;i<this->m_Databuf.size();i++)
332 char c = this->m_Databuf[m_BufStart];
333 m_BufStart++;
334 m_Databuf.m_critSec.Unlock();
335 ParserCmdOutput(c);
337 m_Databuf.m_critSec.Lock();
340 if(m_BufStart>1000)
342 m_Databuf.erase(m_Databuf.begin(), m_Databuf.begin()+m_BufStart);
343 m_BufStart =0;
345 m_Databuf.m_critSec.Unlock();
348 else
349 ParserCmdOutput((char)lParam);
351 return 0;
354 //static function, Share with SyncDialog
355 int CProgressDlg::FindPercentage(CString &log)
357 int s1=log.Find(_T('%'));
358 if(s1<0)
359 return -1;
361 int s2=s1-1;
362 for(int i=s1-1;i>=0;i--)
364 if(log[i]>=_T('0') && log[i]<=_T('9'))
365 s2=i;
366 else
367 break;
369 return _ttol(log.Mid(s2,s1-s2));
372 void CProgressDlg::ParserCmdOutput(char ch)
374 ParserCmdOutput(this->m_Log,this->m_Progress,this->m_hWnd,this->m_pTaskbarList,this->m_LogTextA,ch,&this->m_CurrentWork);
376 int CProgressDlg::ClearESC(CStringA &str)
378 return str.Replace("\033[K", "");
380 void CProgressDlg::ParserCmdOutput(CRichEditCtrl &log,CProgressCtrl &progressctrl,HWND m_hWnd,CComPtr<ITaskbarList3> m_pTaskbarList,CStringA &oneline, char ch, CWnd *CurrentWork)
382 //TRACE(_T("%c"),ch);
383 if( ch == ('\r') || ch == ('\n'))
385 CString str;
387 // TRACE(_T("End Char %s \r\n"),ch==_T('\r')?_T("lf"):_T(""));
388 // TRACE(_T("End Char %s \r\n"),ch==_T('\n')?_T("cr"):_T(""));
390 if(ClearESC(oneline))
392 ch = ('\r');
395 int lines = log.GetLineCount();
396 g_Git.StringAppend(&str,(BYTE*)oneline.GetBuffer(),CP_ACP);
397 str.Trim();
398 // TRACE(_T("%s"), str);
400 if(ch == ('\r'))
402 int start=log.LineIndex(lines-1);
403 log.SetSel(start, log.GetTextLength());
404 log.ReplaceSel(str);
406 else
408 int length = log.GetWindowTextLength();
409 log.SetSel(length, length);
410 if (length > 0)
411 log.ReplaceSel(_T("\r\n") + str);
412 else
413 log.ReplaceSel(str);
416 if (lines > 500) //limited log length
418 int end=log.LineIndex(1);
419 log.SetSel(0,end);
420 log.ReplaceSel(_T(""));
422 log.LineScroll(log.GetLineCount() - log.GetFirstVisibleLine() - 4);
424 int s1=oneline.ReverseFind(_T(':'));
425 int s2=oneline.Find(_T('%'));
426 if (s1 > 0 && s2 > 0)
428 if(CurrentWork)
429 CurrentWork->SetWindowTextW(str.Left(s1));
431 int pos=FindPercentage(str);
432 TRACE(_T("Pos %d\r\n"),pos);
433 if(pos>0)
435 progressctrl.SetPos(pos);
436 if (m_pTaskbarList)
438 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
439 m_pTaskbarList->SetProgressValue(m_hWnd, pos, 100);
444 oneline="";
447 else
449 oneline+=ch;
452 void CProgressDlg::RemoveLastLine(CString &str)
454 int start;
455 start=str.ReverseFind(_T('\n'));
456 if(start>0)
457 str=str.Left(start);
458 return;
460 // CProgressDlg message handlers
462 void CProgressDlg::OnBnClickedOk()
464 m_Log.GetWindowText(this->m_LogText);
465 OnOK();
468 void CProgressDlg::OnBnClickedButton1()
470 this->EndDialog(IDC_PROGRESS_BUTTON1 + this->m_ctrlPostCmd.GetCurrentEntry());
473 void CProgressDlg::OnClose()
475 DialogEnableWindow(IDCANCEL, TRUE);
476 __super::OnClose();
479 void CProgressDlg::OnCancel()
481 m_bAbort = true;
482 if(m_bDone)
484 CResizableStandAloneDialog::OnCancel();
485 return;
488 if( g_Git.m_CurrentGitPi.hProcess )
490 if(::GenerateConsoleCtrlEvent(CTRL_C_EVENT,0))
492 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
494 else
496 GetLastError();
499 KillProcessTree(g_Git.m_CurrentGitPi.dwProcessId);
502 ::WaitForSingleObject(g_Git.m_CurrentGitPi.hProcess ,10000);
503 CResizableStandAloneDialog::OnCancel();
506 void CProgressDlg::KillProcessTree(DWORD dwProcessId)
508 // recursively kills a process tree
509 // This is not optimized, but works and isn't called very often ;)
510 PROCESSENTRY32 pe;
511 memset(&pe, 0, sizeof(PROCESSENTRY32));
512 pe.dwSize = sizeof(PROCESSENTRY32);
514 HANDLE hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
516 if (::Process32First(hSnap, &pe))
520 if (pe.th32ParentProcessID == dwProcessId)
521 KillProcessTree(pe.th32ProcessID);
522 } while (::Process32Next(hSnap, &pe));
524 HANDLE hProc = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
525 if (hProc)
527 ::TerminateProcess(hProc, 1);
528 ::CloseHandle(hProc);
531 ::CloseHandle(hSnap);
534 void CProgressDlg::InsertCRLF()
536 m_Databuf.m_critSec.Lock();
537 for(int i=0;i<m_Databuf.size();i++)
539 if(m_Databuf[i]==('\n'))
541 if(i==0 || m_Databuf[i-1]!= ('\r'))
543 m_Databuf.insert(m_Databuf.begin()+i,('\r'));
544 i++;
548 m_Databuf.m_critSec.Unlock();
551 void CProgressDlg::InsertColorText(CRichEditCtrl &edit,CString text,COLORREF rgb)
553 CHARFORMAT old,cf;
554 edit.GetDefaultCharFormat(cf);
555 old=cf;
556 cf.dwMask|=CFM_COLOR;
557 cf.crTextColor=rgb;
558 cf.dwEffects|=CFE_BOLD;
559 cf.dwEffects &= ~CFE_AUTOCOLOR ;
560 edit.SetSel(edit.GetTextLength()-1,edit.GetTextLength());
561 edit.ReplaceSel(text);
562 edit.SetSel(edit.LineIndex(edit.GetLineCount()-2),edit.GetTextLength());
563 edit.SetSelectionCharFormat(cf);
564 edit.SetSel(edit.GetTextLength(),edit.GetTextLength());
565 edit.SetDefaultCharFormat(old);
566 edit.LineScroll(edit.GetLineCount() - edit.GetFirstVisibleLine() - 4);
569 CString CCommitProgressDlg::Convert2UnionCode(char *buff, int size)
571 CString str;
573 CString cmd, output;
574 int cp=CP_UTF8;
576 cmd=_T("git.exe config i18n.logOutputEncoding");
577 if(g_Git.Run(cmd, &output, NULL, CP_ACP))
578 cp=CP_UTF8;
580 int start=0;
581 output=output.Tokenize(_T("\n"),start);
582 cp=CUnicodeUtils::GetCPCode(output);
584 start =0;
585 if(size == -1)
586 size=strlen(buff);
588 for(int i=0;i<size;i++)
590 if(buff[i] == ']')
591 start = i;
592 if( start >0 && buff[i] =='\n' )
594 start =i;
595 break;
599 str.Empty();
600 g_Git.StringAppend(&str, (BYTE*)buff, cp, start);
601 g_Git.StringAppend(&str, (BYTE*)buff+start, CP_ACP,size - start);
603 return str;
606 LRESULT CProgressDlg::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
608 m_pTaskbarList.Release();
609 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
610 return 0;
613 BOOL CProgressDlg::PreTranslateMessage(MSG* pMsg)
615 if (pMsg->message == WM_KEYDOWN)
617 if (pMsg->wParam == VK_ESCAPE)
619 // pressing the ESC key should close the dialog. But since we disabled the escape
620 // key (so the user doesn't get the idea that he could simply undo an e.g. update)
621 // this won't work.
622 // So if the user presses the ESC key, change it to VK_RETURN so the dialog gets
623 // the impression that the OK button was pressed.
624 if ((!GetDlgItem(IDCANCEL)->IsWindowEnabled())
625 &&(GetDlgItem(IDOK)->IsWindowEnabled())&&(GetDlgItem(IDOK)->IsWindowVisible()))
627 // since we convert ESC to RETURN, make sure the OK button has the focus.
628 GetDlgItem(IDOK)->SetFocus();
629 pMsg->wParam = VK_RETURN;
633 else if (pMsg->message == WM_CONTEXTMENU || pMsg->message == WM_RBUTTONDOWN)
635 CWnd * pWnd = (CWnd*) GetDlgItem(IDC_LOG);
636 if (pWnd == GetFocus())
638 CIconMenu popup;
639 if (popup.CreatePopupMenu())
641 popup.AppendMenuIcon(WM_COPY, IDS_SCIEDIT_COPY, IDI_COPYCLIP);
642 if (m_Log.GetSelText().IsEmpty())
643 popup.EnableMenuItem(WM_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
644 popup.AppendMenu(MF_SEPARATOR);
645 popup.AppendMenuIcon(EM_SETSEL, IDS_SCIEDIT_SELECTALL);
646 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, pMsg->pt.x, pMsg->pt.y, this);
647 switch (cmd)
649 case 0: // no command selected
650 break;
651 case EM_SETSEL:
652 case WM_COPY:
653 ::SendMessage(GetDlgItem(IDC_LOG)->GetSafeHwnd(), cmd, 0, -1);
654 break;
656 return TRUE;
660 return __super::PreTranslateMessage(pMsg);