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
23 #include "TortoiseProc.h"
24 #include "ProgressDlg.h"
27 #include "UnicodeUtils.h"
29 #include "CommonResource.h"
31 // CProgressDlg dialog
33 IMPLEMENT_DYNAMIC(CProgressDlg
, CResizableStandAloneDialog
)
35 CProgressDlg::CProgressDlg(CWnd
* pParent
/*=NULL*/)
36 : CResizableStandAloneDialog(CProgressDlg::IDD
, pParent
), m_bShowCommand(true), m_bAutoCloseOnSuccess(false), m_bAbort(false), m_bDone(false)
39 m_bAltAbortPress
=false;
43 CProgressDlg::~CProgressDlg()
51 void CProgressDlg::DoDataExchange(CDataExchange
* pDX
)
53 CDialog::DoDataExchange(pDX
);
54 DDX_Control(pDX
, IDC_CURRENT
, this->m_CurrentWork
);
55 DDX_Control(pDX
, IDC_TITLE_ANIMATE
, this->m_Animate
);
56 DDX_Control(pDX
, IDC_RUN_PROGRESS
, this->m_Progress
);
57 DDX_Control(pDX
, IDC_LOG
, this->m_Log
);
58 DDX_Control(pDX
, IDC_PROGRESS_BUTTON1
, this->m_ctrlPostCmd
);
61 BEGIN_MESSAGE_MAP(CProgressDlg
, CResizableStandAloneDialog
)
62 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI
, OnProgressUpdateUI
)
63 ON_BN_CLICKED(IDOK
, &CProgressDlg::OnBnClickedOk
)
64 ON_BN_CLICKED(IDC_PROGRESS_BUTTON1
,&CProgressDlg::OnBnClickedButton1
)
65 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED
, OnTaskbarBtnCreated
)
68 BOOL
CProgressDlg::OnInitDialog()
70 CResizableStandAloneDialog::OnInitDialog();
72 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
73 // do this, Explorer would be unable to send that message to our window if we
74 // were running elevated. It's OK to make the call all the time, since if we're
75 // not elevated, this is a no-op.
76 CHANGEFILTERSTRUCT cfs
= { sizeof(CHANGEFILTERSTRUCT
) };
77 typedef BOOL STDAPICALLTYPE
ChangeWindowMessageFilterExDFN(HWND hWnd
, UINT message
, DWORD action
, PCHANGEFILTERSTRUCT pChangeFilterStruct
);
78 HMODULE hUser
= ::LoadLibrary(_T("user32.dll"));
81 ChangeWindowMessageFilterExDFN
*pfnChangeWindowMessageFilterEx
= (ChangeWindowMessageFilterExDFN
*)GetProcAddress(hUser
, "ChangeWindowMessageFilterEx");
82 if (pfnChangeWindowMessageFilterEx
)
84 pfnChangeWindowMessageFilterEx(m_hWnd
, WM_TASKBARBTNCREATED
, MSGFLT_ALLOW
, &cfs
);
88 m_pTaskbarList
.Release();
89 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);
91 AddAnchor(IDC_TITLE_ANIMATE
, TOP_LEFT
, TOP_RIGHT
);
92 AddAnchor(IDC_RUN_PROGRESS
, TOP_LEFT
,TOP_RIGHT
);
93 AddAnchor(IDC_LOG
, TOP_LEFT
,BOTTOM_RIGHT
);
95 AddAnchor(IDOK
,BOTTOM_RIGHT
);
96 AddAnchor(IDCANCEL
,BOTTOM_RIGHT
);
97 AddAnchor(IDC_PROGRESS_BUTTON1
,BOTTOM_LEFT
);
98 AddAnchor(IDC_CURRENT
,TOP_LEFT
);
100 this->GetDlgItem(IDC_PROGRESS_BUTTON1
)->ShowWindow(SW_HIDE
);
101 m_Animate
.Open(IDR_DOWNLOAD
);
104 if ( !m_PreText
.IsEmpty() )
106 InitialText
= m_PreText
+ _T("\r\n");
109 if (m_bShowCommand
&& (!m_GitCmd
.IsEmpty() ))
111 InitialText
+= m_GitCmd
+_T("\r\n\r\n");
114 m_Log
.SetWindowTextW(InitialText
);
115 m_CurrentWork
.SetWindowTextW(_T(""));
117 EnableSaveRestore(_T("ProgressDlg"));
119 m_pThread
= AfxBeginThread(ProgressThreadEntry
, this, THREAD_PRIORITY_NORMAL
,0,CREATE_SUSPENDED
);
122 // ReportError(CString(MAKEINTRESOURCE(IDS_ERR_THREADSTARTFAILED)));
126 m_pThread
->m_bAutoDelete
= FALSE
;
127 m_pThread
->ResumeThread();
130 if(!m_Title
.IsEmpty())
131 this->SetWindowText(m_Title
);
133 if(m_PostCmdList
.GetCount()>0)
134 m_ctrlPostCmd
.AddEntries(m_PostCmdList
);
139 UINT
CProgressDlg::ProgressThreadEntry(LPVOID pVoid
)
141 return ((CProgressDlg
*)pVoid
)->ProgressThread();
144 //static function, Share with SyncDialog
145 UINT
CProgressDlg::RunCmdList(CWnd
*pWnd
,std::vector
<CString
> &cmdlist
,bool bShowCommand
,CString
*pfilename
,bool *bAbort
,CGitByteArray
*pdata
)
149 PROCESS_INFORMATION pi
;
152 memset(&pi
,0,sizeof(PROCESS_INFORMATION
));
154 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_START
,0);
159 for(int i
=0;i
<cmdlist
.size();i
++)
161 if(cmdlist
[i
].IsEmpty())
166 CStringA
str(cmdlist
[i
].Trim()+_T("\n\n"));
167 for(int j
=0;j
<str
.GetLength();j
++)
171 pdata
->m_critSec
.Lock();
172 pdata
->push_back(str
[j
]);
173 pdata
->m_critSec
.Unlock();
176 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_RUN
,str
[j
]);
179 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_RUN
,0);
182 g_Git
.RunAsync(cmdlist
[i
].Trim(),&pi
, &hRead
,pfilename
);
187 while(ReadFile(hRead
,&byte
,1,&readnumber
,NULL
))
194 pdata
->m_critSec
.Lock();
195 pdata
->push_back( byte
);
196 pdata
->m_critSec
.Unlock();
198 if(byte
== '\r' || byte
== '\n')
199 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_RUN
,0);
201 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_RUN
,byte
);
204 CloseHandle(pi
.hThread
);
206 WaitForSingleObject(pi
.hProcess
, INFINITE
);
209 if(!GetExitCodeProcess(pi
.hProcess
,&status
) || *bAbort
)
211 CloseHandle(pi
.hProcess
);
215 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_FAILED
,0);
216 return GIT_ERROR_GET_EXIT_CODE
;
221 CloseHandle(pi
.hProcess
);
225 pWnd
->PostMessage(MSG_PROGRESSDLG_UPDATE_UI
,MSG_PROGRESSDLG_END
,0);
231 UINT
CProgressDlg::ProgressThread()
234 m_GitCmdList
.push_back(m_GitCmd
);
238 if(m_LogFile
.IsEmpty())
241 pfilename
=&m_LogFile
;
243 m_GitStatus
= RunCmdList(this,m_GitCmdList
,m_bShowCommand
,pfilename
,&m_bAbort
,&this->m_Databuf
);;
247 LRESULT
CProgressDlg::OnProgressUpdateUI(WPARAM wParam
,LPARAM lParam
)
249 if(wParam
== MSG_PROGRESSDLG_START
)
252 m_Animate
.Play(0,-1,-1);
253 this->DialogEnableWindow(IDOK
,FALSE
);
256 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
257 m_pTaskbarList
->SetProgressValue(m_hWnd
, 0, 100);
260 if(wParam
== MSG_PROGRESSDLG_END
|| wParam
== MSG_PROGRESSDLG_FAILED
)
264 m_Databuf
.m_critSec
.Lock();
265 m_Databuf
.push_back(0);
266 m_Databuf
.m_critSec
.Unlock();
268 m_Databuf
.m_critSec
.Lock();
269 m_Log
.SetWindowText(Convert2UnionCode((char*)&m_Databuf
[0]));
270 m_Databuf
.m_critSec
.Unlock();
271 m_Log
.LineScroll(m_Log
.GetLineCount() - m_Log
.GetFirstVisibleLine() - 4);
274 m_Databuf
.m_critSec
.Lock();
275 this->m_Databuf
.clear();
276 m_Databuf
.m_critSec
.Unlock();
280 m_Progress
.SetPos(100);
281 this->DialogEnableWindow(IDOK
,TRUE
);
284 err
.Format(_T("\r\nFailed 0x%x (git returned a wrong return code at some time)\r\n"),m_GitStatus
);
285 if(this->m_GitStatus
)
289 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_ERROR
);
290 m_pTaskbarList
->SetProgressValue(m_hWnd
, 100, 100);
292 //InsertColorText(this->m_Log,err,RGB(255,0,0));
296 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NOPROGRESS
);
297 InsertColorText(this->m_Log
,_T("\r\nSuccess\r\n"),RGB(0,0,255));
298 this->DialogEnableWindow(IDCANCEL
,FALSE
);
301 if(wParam
== MSG_PROGRESSDLG_END
&& m_GitStatus
== 0)
303 if(m_bAutoCloseOnSuccess
)
306 if(m_PostCmdList
.GetCount() > 0)
308 //GetDlgItem(IDC_PROGRESS_BUTTON1)->SetWindowText(m_changeAbortButtonOnSuccessTo);
309 GetDlgItem(IDC_PROGRESS_BUTTON1
)->ShowWindow(SW_SHOW
);
310 //GetDlgItem(IDCANCEL)->ShowWindow(SW_HIDE);
311 //Set default button is "close" rather than "push"
312 this->SendMessage(WM_NEXTDLGCTL
, (WPARAM
)GetDlgItem(IDOK
)->m_hWnd
, TRUE
);
321 m_Databuf
.m_critSec
.Lock();
322 for(int i
=this->m_BufStart
;i
<this->m_Databuf
.size();i
++)
324 char c
= this->m_Databuf
[m_BufStart
];
326 m_Databuf
.m_critSec
.Unlock();
329 m_Databuf
.m_critSec
.Lock();
334 m_Databuf
.erase(m_Databuf
.begin(), m_Databuf
.begin()+m_BufStart
);
337 m_Databuf
.m_critSec
.Unlock();
340 ParserCmdOutput((char)lParam
);
345 //static function, Share with SyncDialog
346 int CProgressDlg::FindPercentage(CString
&log
)
348 int s1
=log
.Find(_T('%'));
353 for(int i
=s1
-1;i
>=0;i
--)
355 if(log
[i
]>=_T('0') && log
[i
]<=_T('9'))
360 return _ttol(log
.Mid(s2
,s1
-s2
));
363 void CProgressDlg::ParserCmdOutput(char ch
)
365 ParserCmdOutput(this->m_Log
,this->m_Progress
,this->m_hWnd
,this->m_pTaskbarList
,this->m_LogTextA
,ch
,&this->m_CurrentWork
);
367 int CProgressDlg::ClearESC(CStringA
&str
)
369 return str
.Replace("\033[K","");
371 void CProgressDlg::ParserCmdOutput(CRichEditCtrl
&log
,CProgressCtrl
&progressctrl
,HWND m_hWnd
,CComPtr
<ITaskbarList3
> m_pTaskbarList
,CStringA
&oneline
, char ch
, CWnd
*CurrentWork
)
373 //TRACE(_T("%c"),ch);
374 if( ch
== ('\r') || ch
== ('\n'))
378 // TRACE(_T("End Char %s \r\n"),ch==_T('\r')?_T("lf"):_T(""));
379 // TRACE(_T("End Char %s \r\n"),ch==_T('\n')?_T("cr"):_T(""));
381 if(ClearESC(oneline
))
386 int lines
= log
.GetLineCount();
387 g_Git
.StringAppend(&str
,(BYTE
*)oneline
.GetBuffer(),CP_ACP
);
389 // TRACE(_T("%s"), str);
393 int start
=log
.LineIndex(lines
-1);
394 int length
=log
.LineLength(lines
-1)+1;
395 log
.SetSel(start
, start
+ length
);
400 int length
= log
.GetWindowTextLength();
401 log
.SetSel(length
, length
);
403 log
.ReplaceSel(_T("\r\n") + str
);
408 if (lines
> 500) //limited log length
410 int end
=log
.LineIndex(1);
412 log
.ReplaceSel(_T(""));
414 log
.LineScroll(log
.GetLineCount() - log
.GetFirstVisibleLine() - 4);
416 int s1
=oneline
.ReverseFind(_T(':'));
417 int s2
=oneline
.Find(_T('%'));
418 if (s1
> 0 && s2
> 0)
421 CurrentWork
->SetWindowTextW(str
.Left(s1
));
423 int pos
=FindPercentage(str
);
424 TRACE(_T("Pos %d\r\n"),pos
);
427 progressctrl
.SetPos(pos
);
430 m_pTaskbarList
->SetProgressState(m_hWnd
, TBPF_NORMAL
);
431 m_pTaskbarList
->SetProgressValue(m_hWnd
, pos
, 100);
444 void CProgressDlg::RemoveLastLine(CString
&str
)
447 start
=str
.ReverseFind(_T('\n'));
452 // CProgressDlg message handlers
454 void CProgressDlg::OnBnClickedOk()
456 m_Log
.GetWindowText(this->m_LogText
);
460 void CProgressDlg::OnBnClickedButton1()
462 this->EndDialog(IDC_PROGRESS_BUTTON1
+ this->m_ctrlPostCmd
.GetCurrentEntry());
464 void CProgressDlg::OnCancel()
469 CResizableStandAloneDialog::OnCancel();
473 if( g_Git
.m_CurrentGitPi
.hProcess
)
475 if(::GenerateConsoleCtrlEvent(CTRL_C_EVENT
,0))
477 ::WaitForSingleObject(g_Git
.m_CurrentGitPi
.hProcess
,10000);
483 HANDLE hProcessHandle
= ::OpenProcess( PROCESS_TERMINATE
, FALSE
,g_Git
.m_CurrentGitPi
.dwProcessId
);
485 if(!::TerminateProcess(hProcessHandle
,-1) )
491 ::WaitForSingleObject(g_Git
.m_CurrentGitPi
.hProcess
,10000);
492 CResizableStandAloneDialog::OnCancel();
496 void CProgressDlg::InsertCRLF()
498 m_Databuf
.m_critSec
.Lock();
499 for(int i
=0;i
<m_Databuf
.size();i
++)
501 if(m_Databuf
[i
]==('\n'))
503 if(i
==0 || m_Databuf
[i
-1]!= ('\r'))
505 m_Databuf
.insert(m_Databuf
.begin()+i
,('\r'));
510 m_Databuf
.m_critSec
.Unlock();
513 void CProgressDlg::InsertColorText(CRichEditCtrl
&edit
,CString text
,COLORREF rgb
)
516 edit
.GetDefaultCharFormat(cf
);
518 cf
.dwMask
|=CFM_COLOR
;
520 cf
.dwEffects
|=CFE_BOLD
;
521 cf
.dwEffects
&= ~CFE_AUTOCOLOR
;
522 edit
.SetSel(edit
.GetTextLength()-1,edit
.GetTextLength());
523 edit
.ReplaceSel(text
);
524 edit
.SetSel(edit
.LineIndex(edit
.GetLineCount()-2),edit
.GetTextLength());
525 edit
.SetSelectionCharFormat(cf
);
526 edit
.SetSel(edit
.GetTextLength(),edit
.GetTextLength());
527 edit
.SetDefaultCharFormat(old
);
528 edit
.LineScroll(edit
.GetLineCount() - edit
.GetFirstVisibleLine() - 4);
531 CString
CCommitProgressDlg::Convert2UnionCode(char *buff
, int size
)
538 cmd
=_T("git.exe config i18n.logOutputEncoding");
539 if(g_Git
.Run(cmd
,&output
,CP_ACP
))
543 output
=output
.Tokenize(_T("\n"),start
);
544 cp
=CUnicodeUtils::GetCPCode(output
);
550 for(int i
=0;i
<size
;i
++)
554 if( start
>0 && buff
[i
] =='\n' )
562 g_Git
.StringAppend(&str
, (BYTE
*)buff
, cp
, start
);
563 g_Git
.StringAppend(&str
, (BYTE
*)buff
+start
, CP_ACP
,size
- start
);
568 LRESULT
CProgressDlg::OnTaskbarBtnCreated(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
570 m_pTaskbarList
.Release();
571 m_pTaskbarList
.CoCreateInstance(CLSID_TaskbarList
);
575 BOOL
CProgressDlg::PreTranslateMessage(MSG
* pMsg
)
577 if (pMsg
->message
== WM_CONTEXTMENU
|| pMsg
->message
== WM_RBUTTONDOWN
)
579 CWnd
* pWnd
= (CWnd
*) GetDlgItem(IDC_LOG
);
580 if (pWnd
== GetFocus())
583 if (popup
.CreatePopupMenu())
585 popup
.AppendMenuIcon(WM_COPY
, IDS_SCIEDIT_COPY
, IDI_COPYCLIP
);
586 if (m_Log
.GetSelText().IsEmpty())
587 popup
.EnableMenuItem(WM_COPY
, MF_BYCOMMAND
| MF_DISABLED
| MF_GRAYED
);
588 popup
.AppendMenu(MF_SEPARATOR
);
589 popup
.AppendMenuIcon(EM_SETSEL
, IDS_SCIEDIT_SELECTALL
);
590 int cmd
= popup
.TrackPopupMenu(TPM_RETURNCMD
| TPM_LEFTALIGN
| TPM_NONOTIFY
, pMsg
->pt
.x
, pMsg
->pt
.y
, this);
593 case 0: // no command selected
597 ::SendMessage(GetDlgItem(IDC_LOG
)->GetSafeHwnd(), cmd
, 0, -1);
604 return __super::PreTranslateMessage(pMsg
);