Allow to clear filters with escape
[TortoiseGit.git] / src / TortoiseProc / ProgressDlg.cpp
blobd509ba138daee71c1ca24108e3cb3490525d212c
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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 "UnicodeUtils.h"
26 #include "IconMenu.h"
27 #include "LoglistCommonResource.h"
28 #include <Tlhelp32.h>
29 #include "AppUtils.h"
30 #include "SmartHandle.h"
31 #include "../TGitCache/CacheInterface.h"
32 #include "LoglistUtils.h"
33 #include "MessageBox.h"
34 #include "LogFile.h"
35 #include "CmdLineParser.h"
36 #include "StringUtils.h"
38 // CProgressDlg dialog
40 IMPLEMENT_DYNAMIC(CProgressDlg, CResizableStandAloneDialog)
42 CProgressDlg::CProgressDlg(CWnd* pParent /*=nullptr*/)
43 : CResizableStandAloneDialog(CProgressDlg::IDD, pParent)
44 , m_bShowCommand(true)
45 , m_bAbort(false)
46 , m_bDone(false)
47 , m_startTick(GetTickCount64())
48 , m_BufStart(0)
49 , m_Git(&g_Git)
50 , m_hAccel(nullptr)
52 m_pThread = nullptr;
53 m_bBufferAll=false;
54 m_GitStatus = (DWORD)-1;
55 int autoClose = CRegDWORD(_T("Software\\TortoiseGit\\AutoCloseGitProgress"), 0);
56 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
57 if (parser.HasKey(_T("closeonend")))
58 autoClose = parser.GetLongVal(_T("closeonend"));
59 switch (autoClose)
61 case 1:
62 m_AutoClose = AUTOCLOSE_IF_NO_OPTIONS;
63 break;
64 case 2:
65 m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
66 break;
67 default:
68 m_AutoClose = AUTOCLOSE_NO;
69 break;
73 CProgressDlg::~CProgressDlg()
75 if (m_hAccel)
76 DestroyAcceleratorTable(m_hAccel);
77 delete m_pThread;
80 void CProgressDlg::DoDataExchange(CDataExchange* pDX)
82 CDialog::DoDataExchange(pDX);
83 DDX_Control(pDX, IDC_CURRENT, this->m_CurrentWork);
84 DDX_Control(pDX, IDC_TITLE_ANIMATE, this->m_Animate);
85 DDX_Control(pDX, IDC_RUN_PROGRESS, this->m_Progress);
86 DDX_Control(pDX, IDC_LOG, this->m_Log);
87 DDX_Control(pDX, IDC_PROGRESS_BUTTON1, this->m_ctrlPostCmd);
90 BEGIN_MESSAGE_MAP(CProgressDlg, CResizableStandAloneDialog)
91 ON_WM_CLOSE()
92 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
93 ON_BN_CLICKED(IDOK, &CProgressDlg::OnBnClickedOk)
94 ON_BN_CLICKED(IDC_PROGRESS_BUTTON1,&CProgressDlg::OnBnClickedButton1)
95 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
96 END_MESSAGE_MAP()
98 BOOL CProgressDlg::OnInitDialog()
100 CResizableStandAloneDialog::OnInitDialog();
102 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
103 // do this, Explorer would be unable to send that message to our window if we
104 // were running elevated. It's OK to make the call all the time, since if we're
105 // not elevated, this is a no-op.
106 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
107 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
108 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
109 if (hUser)
111 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
112 if (pfnChangeWindowMessageFilterEx)
113 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
115 m_pTaskbarList.Release();
116 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
117 m_pTaskbarList = nullptr;
119 AddAnchor(IDC_TITLE_ANIMATE, TOP_LEFT, TOP_RIGHT);
120 AddAnchor(IDC_RUN_PROGRESS, TOP_LEFT,TOP_RIGHT);
121 AddAnchor(IDC_LOG, TOP_LEFT,BOTTOM_RIGHT);
123 AddAnchor(IDOK,BOTTOM_RIGHT);
124 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
125 AddAnchor(IDC_PROGRESS_BUTTON1,BOTTOM_LEFT);
126 AddAnchor(IDC_CURRENT,TOP_LEFT);
128 this->GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_HIDE);
129 m_Animate.Open(IDR_DOWNLOAD);
131 CFont m_logFont;
132 CAppUtils::CreateFontForLogs(m_logFont);
133 //GetDlgItem(IDC_CMD_LOG)->SetFont(&m_logFont);
134 m_Log.SetFont(&m_logFont);
136 CString InitialText;
137 if ( !m_PreText.IsEmpty() )
138 InitialText = m_PreText + _T("\r\n");
139 #if 0
140 if (m_bShowCommand && (!m_GitCmd.IsEmpty() ))
141 InitialText += m_GitCmd+_T("\r\n\r\n");
142 #endif
143 m_Log.SetWindowTextW(InitialText);
144 m_CurrentWork.SetWindowTextW(_T(""));
146 if (!m_PreFailText.IsEmpty())
147 InsertColorText(this->m_Log, m_PreFailText, RGB(255, 0, 0));
149 EnableSaveRestore(_T("ProgressDlg"));
151 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
152 if (!m_pThread)
154 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
155 DialogEnableWindow(IDCANCEL, TRUE);
157 else
159 m_pThread->m_bAutoDelete = FALSE;
160 m_pThread->ResumeThread();
163 CString sWindowTitle;
164 GetWindowText(sWindowTitle);
165 CAppUtils::SetWindowTitle(m_hWnd, m_Git->m_CurrentDir, sWindowTitle);
167 // Make sure this dialog is shown in foreground (see issue #1536)
168 SetForegroundWindow();
170 return TRUE;
173 static void EnsurePostMessage(CWnd *pWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
175 redo:
176 if (!pWnd->PostMessage(Msg, wParam, lParam))
178 if (GetLastError() == ERROR_NOT_ENOUGH_QUOTA)
180 Sleep(20);
181 goto redo;
183 else
184 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Message %d-%d could not be sent (error %d; %s)\n"), wParam, lParam, GetLastError(), (LPCTSTR)CFormatMessageWrapper());
188 UINT CProgressDlg::ProgressThreadEntry(LPVOID pVoid)
190 return ((CProgressDlg*)pVoid)->ProgressThread();
193 //static function, Share with SyncDialog
194 UINT CProgressDlg::RunCmdList(CWnd* pWnd, STRING_VECTOR& cmdlist, STRING_VECTOR& dirlist, bool bShowCommand, CString* pfilename, volatile bool* bAbort, CGitGuardedByteArray* pdata, CGit* git)
196 UINT ret=0;
198 std::vector<std::unique_ptr<CBlockCacheForPath>> cacheBlockList;
199 std::vector<std::unique_ptr<CGit>> gitList;
200 if (dirlist.empty())
201 cacheBlockList.push_back(std::make_unique<CBlockCacheForPath>(git->m_CurrentDir));
202 else
204 for (const auto& dir : dirlist)
206 auto pGit = std::make_unique<CGit>();
207 pGit->m_CurrentDir = dir;
208 gitList.push_back(std::move(pGit));
209 cacheBlockList.push_back(std::make_unique<CBlockCacheForPath>(dir));
213 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_START, 0);
215 if(pdata)
216 pdata->clear();
218 for (size_t i = 0; i < cmdlist.size(); ++i)
220 if(cmdlist[i].IsEmpty())
221 continue;
223 if (bShowCommand)
225 CStringA str;
226 if (gitList.empty() || gitList.size() == 1 && gitList[0]->m_CurrentDir == git->m_CurrentDir)
227 str = CUnicodeUtils::GetMulti(cmdlist[i].Trim() + _T("\r\n\r\n"), CP_UTF8);
228 else
229 str = CUnicodeUtils::GetMulti((i > 0 ? _T("\r\n") : _T("")) + gitList[i]->m_CurrentDir + _T("\r\n") + cmdlist[i].Trim() + _T("\r\n\r\n"), CP_UTF8);
230 for (int j = 0; j < str.GetLength(); ++j)
232 if(pdata)
234 pdata->m_critSec.Lock();
235 pdata->push_back(str[j]);
236 pdata->m_critSec.Unlock();
238 else
239 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,str[j]);
241 if(pdata)
242 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
245 PROCESS_INFORMATION pi;
246 CAutoGeneralHandle hRead;
247 int runAsyncRet = -1;
248 if (gitList.empty())
249 runAsyncRet = git->RunAsync(cmdlist[i].Trim(), &pi, hRead.GetPointer(), nullptr, pfilename);
250 else
251 runAsyncRet = gitList[i]->RunAsync(cmdlist[i].Trim(), &pi, hRead.GetPointer(), nullptr, pfilename);
252 if (runAsyncRet)
254 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_FAILED, -1 * runAsyncRet);
255 return runAsyncRet;
258 CAutoGeneralHandle piProcess(pi.hProcess);
259 CAutoGeneralHandle piThread(pi.hThread);
260 DWORD readnumber;
261 char lastByte = '\0';
262 char byte;
263 CString output;
264 while (ReadFile(hRead, &byte, 1, &readnumber, nullptr))
266 if(pdata)
268 if(byte == 0)
269 byte = '\n';
271 pdata->m_critSec.Lock();
272 if (byte == '\n' && lastByte != '\r')
273 pdata->push_back('\r');
274 pdata->push_back( byte);
275 lastByte = byte;
276 pdata->m_critSec.Unlock();
278 if(byte == '\r' || byte == '\n')
279 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
281 else
282 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,byte);
284 if (pdata)
286 pdata->m_critSec.Lock();
287 bool post = !pdata->empty();
288 pdata->m_critSec.Unlock();
289 if (post)
290 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_RUN, 0);
293 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": waiting for process to finish (%s), aborted: %d\n"), (LPCTSTR)cmdlist[i], *bAbort);
295 WaitForSingleObject(pi.hProcess, INFINITE);
297 DWORD status=0;
298 if(!GetExitCodeProcess(pi.hProcess,&status) || *bAbort)
300 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": process %s finished, status code could not be fetched, (error %d; %s), aborted: %d\n"), (LPCTSTR)cmdlist[i], GetLastError(), (LPCTSTR)CFormatMessageWrapper(), *bAbort);
302 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_FAILED, status);
303 return TGIT_GIT_ERROR_GET_EXIT_CODE;
305 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": process %s finished with code %d\n"), (LPCTSTR)cmdlist[i], status);
306 ret |= status;
309 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_END, ret);
311 return ret;
314 UINT CProgressDlg::ProgressThread()
316 if (!m_GitCmd.IsEmpty())
317 m_GitCmdList.push_back(m_GitCmd);
319 CString *pfilename;
321 if(m_LogFile.IsEmpty())
322 pfilename = nullptr;
323 else
324 pfilename=&m_LogFile;
326 m_startTick = GetTickCount64();
327 m_GitStatus = RunCmdList(this, m_GitCmdList, m_GitDirList, m_bShowCommand, pfilename, &m_bAbort, &this->m_Databuf, m_Git);
328 return 0;
331 LRESULT CProgressDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
333 if(wParam == MSG_PROGRESSDLG_START)
335 m_BufStart = 0 ;
336 m_Animate.Play(0, INT_MAX, INT_MAX);
337 DialogEnableWindow(IDCANCEL, TRUE);
338 if (m_pTaskbarList)
340 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
341 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
344 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
346 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": got message: %d\n"), wParam);
347 ULONGLONG tickSpent = GetTickCount64() - m_startTick;
348 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
350 if(m_bBufferAll)
352 m_Databuf.m_critSec.Lock();
353 m_Databuf.push_back(0);
354 m_Log.SetWindowText(Convert2UnionCode((char*)&m_Databuf[0]));
355 m_Databuf.m_critSec.Unlock();
357 m_BufStart=0;
358 m_Databuf.m_critSec.Lock();
359 this->m_Databuf.clear();
360 m_Databuf.m_critSec.Unlock();
362 m_bDone = true;
363 m_Animate.Stop();
364 m_Progress.SetPos(100);
365 this->DialogEnableWindow(IDOK,TRUE);
367 m_GitStatus = (DWORD)lParam;
368 if (m_GitCmd.IsEmpty() && m_GitCmdList.empty())
369 m_GitStatus = (DWORD)-1;
371 // detect crashes of perl when performing git svn actions
372 if (m_GitStatus == 0 && m_GitCmd.Find(_T(" svn ")) > 1)
374 CString log;
375 m_Log.GetWindowText(log);
376 if (log.GetLength() > 18 && log.Mid(log.GetLength() - 18) == _T("perl.exe.stackdump"))
377 m_GitStatus = (DWORD)-1;
380 if (m_PostExecCallback)
382 CString extraMsg;
383 m_PostExecCallback(m_GitStatus, extraMsg);
384 if (!extraMsg.IsEmpty())
386 int start = m_Log.GetTextLength();
387 m_Log.SetSel(start, start);
388 m_Log.ReplaceSel(extraMsg);
392 if(this->m_GitStatus)
394 if (m_pTaskbarList)
396 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
397 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
399 CString log;
400 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, m_GitStatus);
401 CString err;
402 if (CRegDWORD(_T("Software\\TortoiseGit\\ShowGitexeTimings"), TRUE))
403 err.Format(_T("\r\n\r\n%s (%I64u ms @ %s)\r\n"), (LPCTSTR)log, tickSpent, (LPCTSTR)strEndTime);
404 else
405 err.Format(_T("\r\n\r\n%s\r\n"), (LPCTSTR)log);
406 if (!m_GitCmd.IsEmpty() || !m_GitCmdList.empty())
407 InsertColorText(this->m_Log, err, RGB(255,0,0));
408 if (CRegDWORD(_T("Software\\TortoiseGit\\NoSounds"), FALSE) == FALSE)
409 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMEXCLAMATION, nullptr, SND_ALIAS_ID | SND_ASYNC);
411 else {
412 if (m_pTaskbarList)
413 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
414 CString temp;
415 temp.LoadString(IDS_SUCCESS);
416 CString log;
417 if (CRegDWORD(_T("Software\\TortoiseGit\\ShowGitexeTimings"), TRUE))
418 log.Format(_T("\r\n%s (%I64u ms @ %s)\r\n"), (LPCTSTR)temp, tickSpent, (LPCTSTR)strEndTime);
419 else
420 log.Format(_T("\r\n%s\r\n"), (LPCTSTR)temp);
421 InsertColorText(this->m_Log, log, RGB(0,0,255));
422 this->DialogEnableWindow(IDCANCEL,FALSE);
425 m_Log.PostMessage(WM_VSCROLL, SB_BOTTOM, 0);
427 if (wParam == MSG_PROGRESSDLG_END)
429 if (m_PostCmdCallback) // new handling method using callback
431 m_PostCmdCallback(m_GitStatus, m_PostCmdList);
433 if (!m_PostCmdList.empty())
435 int i = 0;
436 for (const auto& entry : m_PostCmdList)
438 ++i;
439 m_ctrlPostCmd.AddEntry(entry.icon, entry.label);
440 TCHAR accellerator = CStringUtils::GetAccellerator(entry.label);
441 if (accellerator == L'\0')
442 continue;
443 ++m_accellerators[accellerator].cnt;
444 if (m_accellerators[accellerator].cnt > 1)
445 m_accellerators[accellerator].id = -1;
446 else
447 m_accellerators[accellerator].id = i - 1;
450 if (m_accellerators.size())
452 LPACCEL lpaccelNew = (LPACCEL)LocalAlloc(LPTR, m_accellerators.size() * sizeof(ACCEL));
453 SCOPE_EXIT { LocalFree(lpaccelNew); };
454 i = 0;
455 for (auto& entry : m_accellerators)
457 lpaccelNew[i].cmd = (WORD)(WM_USER + 1 + entry.second.id);
458 lpaccelNew[i].fVirt = FVIRTKEY | FALT;
459 lpaccelNew[i].key = entry.first;
460 entry.second.wmid = lpaccelNew[i].cmd;
461 ++i;
463 m_hAccel = CreateAcceleratorTable(lpaccelNew, (int)m_accellerators.size());
465 GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_SHOW);
470 if(wParam == MSG_PROGRESSDLG_END && m_GitStatus == 0)
472 if (m_AutoClose == AUTOCLOSE_IF_NO_OPTIONS && m_PostCmdList.empty() || m_AutoClose == AUTOCLOSE_IF_NO_ERRORS)
473 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
477 if(!m_bBufferAll)
479 if(lParam == 0)
481 m_Databuf.m_critSec.Lock();
482 for (size_t i = this->m_BufStart; i < this->m_Databuf.size(); ++i)
484 char c = this->m_Databuf[m_BufStart];
485 ++m_BufStart;
486 m_Databuf.m_critSec.Unlock();
487 ParserCmdOutput(c);
489 m_Databuf.m_critSec.Lock();
492 if(m_BufStart>1000)
494 m_Databuf.erase(m_Databuf.cbegin(), m_Databuf.cbegin() + m_BufStart);
495 m_BufStart =0;
497 m_Databuf.m_critSec.Unlock();
500 else
501 ParserCmdOutput((char)lParam);
503 return 0;
506 //static function, Share with SyncDialog
507 int CProgressDlg::FindPercentage(CString &log)
509 int s1=log.Find(_T('%'));
510 if(s1<0)
511 return -1;
513 int s2=s1-1;
514 for(int i=s1-1;i>=0;i--)
516 if(log[i]>=_T('0') && log[i]<=_T('9'))
517 s2=i;
518 else
519 break;
521 return _ttol(log.Mid(s2,s1-s2));
524 void CProgressDlg::ParserCmdOutput(char ch)
526 ParserCmdOutput(this->m_Log,this->m_Progress,this->m_hWnd,this->m_pTaskbarList,this->m_LogTextA,ch,&this->m_CurrentWork);
528 void CProgressDlg::ClearESC(CString &str)
530 // see http://ascii-table.com/ansi-escape-sequences.php and http://tldp.org/HOWTO/Bash-Prompt-HOWTO/c327.html
531 str.Replace(_T("\033[K"), _T("")); // erase until end of line; no need to care for this, because we always clear the whole line
533 // drop colors
534 while (true)
536 int escapePosition = str.Find(_T('\033'));
537 if (escapePosition >= 0 && str.GetLength() >= escapePosition + 3)
539 if (str.Mid(escapePosition, 2) == _T("\033["))
541 int colorEnd = str.Find(_T('m'), escapePosition + 2);
542 if (colorEnd > 0)
544 bool found = true;
545 for (int i = escapePosition + 2; i < colorEnd; ++i)
547 if (str[i] != _T(';') && (str[i] < _T('0') && str[i] > _T('9')))
549 found = false;
550 break;
553 if (found)
555 if (escapePosition > 0)
556 str = str.Left(escapePosition) + str.Mid(colorEnd + 1);
557 else
558 str = str.Mid(colorEnd);
559 continue;
564 break;
567 void CProgressDlg::ParserCmdOutput(CRichEditCtrl &log,CProgressCtrl &progressctrl,HWND m_hWnd,CComPtr<ITaskbarList3> m_pTaskbarList,CStringA &oneline, char ch, CWnd *CurrentWork)
569 //TRACE(_T("%c"),ch);
570 if( ch == ('\r') || ch == ('\n'))
572 CString str = CUnicodeUtils::GetUnicode(oneline);
574 // TRACE(_T("End Char %s \r\n"),ch==_T('\r')?_T("lf"):_T(""));
575 // TRACE(_T("End Char %s \r\n"),ch==_T('\n')?_T("cr"):_T(""));
577 int lines = log.GetLineCount();
578 str.Trim();
579 // TRACE(_T("%s"), str);
581 ClearESC(str);
583 if(ch == ('\r'))
585 int start=log.LineIndex(lines-1);
586 log.SetSel(start, log.GetTextLength());
587 log.ReplaceSel(str);
589 else
591 int length = log.GetWindowTextLength();
592 log.SetSel(length, length);
593 if (length > 0)
594 log.ReplaceSel(_T("\r\n") + str);
595 else
596 log.ReplaceSel(str);
599 if (lines > 500) //limited log length
601 int end=log.LineIndex(1);
602 log.SetSel(0,end);
603 log.ReplaceSel(_T(""));
605 log.PostMessage(WM_VSCROLL, SB_BOTTOM, 0);
607 int s1=oneline.ReverseFind(_T(':'));
608 int s2=oneline.Find(_T('%'));
609 if (s1 > 0 && s2 > 0)
611 if(CurrentWork)
612 CurrentWork->SetWindowTextW(str.Left(s1));
614 int pos=FindPercentage(str);
615 TRACE(_T("Pos %d\r\n"),pos);
616 if(pos>0)
618 progressctrl.SetPos(pos);
619 if (m_pTaskbarList)
621 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
622 m_pTaskbarList->SetProgressValue(m_hWnd, pos, 100);
627 oneline.Empty();
629 else
630 oneline+=ch;
632 void CProgressDlg::RemoveLastLine(CString &str)
634 int start;
635 start=str.ReverseFind(_T('\n'));
636 if(start>0)
637 str=str.Left(start);
639 // CProgressDlg message handlers
641 void CProgressDlg::WriteLog() const
643 CLogFile logfile(g_Git.m_CurrentDir);
644 if (logfile.Open())
646 logfile.AddTimeLine();
647 CString text = GetLogText();
648 LPCTSTR psz_string = text;
649 while (*psz_string)
651 size_t i_len = wcscspn(psz_string, L"\r\n");
652 logfile.AddLine(CString(psz_string, (int)i_len));
653 psz_string += i_len;
654 if (*psz_string == '\r')
656 ++psz_string;
657 if (*psz_string == '\n')
658 ++psz_string;
660 else if (*psz_string == '\n')
661 ++psz_string;
663 if (m_bAbort)
665 CString canceled;
666 canceled.LoadString(IDS_USERCANCELLED);
667 logfile.AddLine(canceled);
669 logfile.Close();
673 void CProgressDlg::OnBnClickedOk()
675 if (m_pThread) // added here because Close-button is "called" from thread by PostMessage
676 ::WaitForSingleObject(m_pThread->m_hThread, 5000);
677 m_Log.GetWindowText(this->m_LogText);
678 WriteLog();
679 OnOK();
682 void CProgressDlg::OnBnClickedButton1()
684 WriteLog();
685 ShowWindow(SW_HIDE);
686 m_PostCmdList.at(m_ctrlPostCmd.GetCurrentEntry()).action();
687 EndDialog(IDOK);
690 void CProgressDlg::OnClose()
692 DialogEnableWindow(IDCANCEL, TRUE);
693 __super::OnClose();
696 void CProgressDlg::OnCancel()
698 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": User canceled\n"));
699 m_bAbort = true;
700 if(m_bDone)
702 WriteLog();
703 CResizableStandAloneDialog::OnCancel();
704 return;
707 if( m_Git->m_CurrentGitPi.hProcess )
709 DWORD dwConfirmKillProcess = CRegDWORD(_T("Software\\TortoiseGit\\ConfirmKillProcess"));
710 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
711 return;
712 if(::GenerateConsoleCtrlEvent(CTRL_C_EVENT,0))
714 ::WaitForSingleObject(m_Git->m_CurrentGitPi.hProcess ,10000);
717 KillProcessTree(m_Git->m_CurrentGitPi.dwProcessId);
720 ::WaitForSingleObject(m_Git->m_CurrentGitPi.hProcess ,10000);
721 if (m_pThread)
723 if (::WaitForSingleObject(m_pThread->m_hThread, 5000) == WAIT_TIMEOUT)
724 g_Git.KillRelatedThreads(m_pThread);
727 WriteLog();
728 CResizableStandAloneDialog::OnCancel();
731 void CProgressDlg::KillProcessTree(DWORD dwProcessId, unsigned int depth)
733 // recursively kills a process tree
734 // This is not optimized, but works and isn't called very often ;)
736 if (!dwProcessId || depth > 20)
737 return;
739 PROCESSENTRY32 pe = { 0 };
740 pe.dwSize = sizeof(PROCESSENTRY32);
742 CAutoGeneralHandle hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
744 if (::Process32First(hSnap, &pe))
748 if (pe.th32ParentProcessID == dwProcessId)
749 KillProcessTree(pe.th32ProcessID, depth + 1);
750 } while (::Process32Next(hSnap, &pe));
752 CAutoGeneralHandle hProc = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
753 if (hProc)
754 ::TerminateProcess(hProc, 1);
758 void CProgressDlg::InsertColorText(CRichEditCtrl &edit,CString text,COLORREF rgb)
760 CHARFORMAT old,cf;
761 edit.GetDefaultCharFormat(cf);
762 old=cf;
763 cf.dwMask|=CFM_COLOR;
764 cf.crTextColor=rgb;
765 cf.dwEffects|=CFE_BOLD;
766 cf.dwEffects &= ~CFE_AUTOCOLOR ;
767 edit.SetSel(edit.GetTextLength()-1,edit.GetTextLength());
768 edit.ReplaceSel(text);
769 edit.SetSel(edit.LineIndex(edit.GetLineCount()-2),edit.GetTextLength());
770 edit.SetSelectionCharFormat(cf);
771 edit.SetSel(edit.GetTextLength(),edit.GetTextLength());
772 edit.SetDefaultCharFormat(old);
775 CString CCommitProgressDlg::Convert2UnionCode(char *buff, int size)
777 int start=0;
778 if(size == -1)
779 size = (int)strlen(buff);
781 for (int i = 0; i < size; ++i)
783 if(buff[i] == ']')
784 start = i;
785 if( start >0 && buff[i] =='\n' )
787 start =i;
788 break;
792 CString str;
793 CGit::StringAppend(&str, (BYTE*)buff, g_Git.m_LogEncode, start);
794 CGit::StringAppend(&str, (BYTE*)buff + start, CP_UTF8, size - start);
796 ClearESC(str);
798 return str;
801 LRESULT CProgressDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
803 m_pTaskbarList.Release();
804 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
805 return __super::OnTaskbarButtonCreated(wParam, lParam);
808 BOOL CProgressDlg::PreTranslateMessage(MSG* pMsg)
810 if (m_hAccel && TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
811 return TRUE;
812 if (pMsg->message == WM_KEYDOWN)
814 if (pMsg->wParam == VK_ESCAPE)
816 // pressing the ESC key should close the dialog. But since we disabled the escape
817 // key (so the user doesn't get the idea that he could simply undo an e.g. update)
818 // this won't work.
819 // So if the user presses the ESC key, change it to VK_RETURN so the dialog gets
820 // the impression that the OK button was pressed.
821 if ((!GetDlgItem(IDCANCEL)->IsWindowEnabled())
822 &&(GetDlgItem(IDOK)->IsWindowEnabled())&&(GetDlgItem(IDOK)->IsWindowVisible()))
824 // since we convert ESC to RETURN, make sure the OK button has the focus.
825 GetDlgItem(IDOK)->SetFocus();
826 pMsg->wParam = VK_RETURN;
830 else if (pMsg->message == WM_CONTEXTMENU || pMsg->message == WM_RBUTTONDOWN)
832 CWnd * pWnd = (CWnd*) GetDlgItem(IDC_LOG);
833 if (pWnd == GetFocus())
835 CIconMenu popup;
836 if (popup.CreatePopupMenu())
838 long start = -1, end = -1;
839 auto pEdit = (CRichEditCtrl *)GetDlgItem(IDC_LOG);
840 pEdit->GetSel(start, end);
841 popup.AppendMenuIcon(WM_COPY, IDS_SCIEDIT_COPY, IDI_COPYCLIP);
842 if (start >= end)
843 popup.EnableMenuItem(WM_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
844 popup.AppendMenu(MF_SEPARATOR);
845 popup.AppendMenuIcon(EM_SETSEL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
846 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, pMsg->pt.x, pMsg->pt.y, this);
847 switch (cmd)
849 case 0: // no command selected
850 break;
851 case EM_SETSEL:
853 pEdit->SetRedraw(FALSE);
854 int oldLine = pEdit->GetFirstVisibleLine();
855 pEdit->SetSel(0, -1);
856 pEdit->Copy();
857 pEdit->SetSel(start, end);
858 int newLine = pEdit->GetFirstVisibleLine();
859 pEdit->LineScroll(oldLine - newLine);
860 pEdit->SetRedraw(TRUE);
861 pEdit->RedrawWindow();
863 break;
864 case WM_COPY:
865 ::SendMessage(GetDlgItem(IDC_LOG)->GetSafeHwnd(), cmd, 0, -1);
866 break;
868 return TRUE;
872 return __super::PreTranslateMessage(pMsg);
875 LRESULT CProgressDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
877 if (m_hAccel && message == WM_COMMAND && LOWORD(wParam) >= WM_USER && LOWORD(wParam) <= WM_USER + m_accellerators.size())
879 for (const auto& entry : m_accellerators)
881 if (entry.second.wmid != LOWORD(wParam))
882 continue;
883 if (entry.second.id == -1)
884 m_ctrlPostCmd.PostMessage(WM_KEYDOWN, VK_F4, NULL);
885 else
887 m_ctrlPostCmd.SetCurrentEntry(entry.second.id);
888 OnBnClickedButton1();
890 return 0;
894 return __super::DefWindowProc(message, wParam, lParam);