Fixed issue #2441: A way to disable the sound that is played on errors
[TortoiseGit.git] / src / TortoiseProc / ProgressDlg.cpp
blobca4a96d781229ba3de1f1d4574d891303ba6f7e9
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2015 - 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 "UnicodeUtils.h"
27 #include "IconMenu.h"
28 #include "LoglistCommonResource.h"
29 #include <Tlhelp32.h>
30 #include "AppUtils.h"
31 #include "SmartHandle.h"
32 #include "../TGitCache/CacheInterface.h"
33 #include "LoglistUtils.h"
34 #include "Win7.h"
35 #include "MessageBox.h"
36 #include "LogFile.h"
37 #include "CmdLineParser.h"
39 // CProgressDlg dialog
41 IMPLEMENT_DYNAMIC(CProgressDlg, CResizableStandAloneDialog)
43 CProgressDlg::CProgressDlg(CWnd* pParent /*=NULL*/)
44 : CResizableStandAloneDialog(CProgressDlg::IDD, pParent)
45 , m_bShowCommand(true)
46 , m_bAbort(false)
47 , m_bDone(false)
48 , m_startTick(GetTickCount())
49 , m_BufStart(0)
50 , m_Git(&g_Git)
52 m_pThread = NULL;
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_pThread != NULL)
77 delete m_pThread;
81 void CProgressDlg::DoDataExchange(CDataExchange* pDX)
83 CDialog::DoDataExchange(pDX);
84 DDX_Control(pDX, IDC_CURRENT, this->m_CurrentWork);
85 DDX_Control(pDX, IDC_TITLE_ANIMATE, this->m_Animate);
86 DDX_Control(pDX, IDC_RUN_PROGRESS, this->m_Progress);
87 DDX_Control(pDX, IDC_LOG, this->m_Log);
88 DDX_Control(pDX, IDC_PROGRESS_BUTTON1, this->m_ctrlPostCmd);
91 BEGIN_MESSAGE_MAP(CProgressDlg, CResizableStandAloneDialog)
92 ON_WM_CLOSE()
93 ON_MESSAGE(MSG_PROGRESSDLG_UPDATE_UI, OnProgressUpdateUI)
94 ON_BN_CLICKED(IDOK, &CProgressDlg::OnBnClickedOk)
95 ON_BN_CLICKED(IDC_PROGRESS_BUTTON1,&CProgressDlg::OnBnClickedButton1)
96 ON_REGISTERED_MESSAGE(WM_TASKBARBTNCREATED, OnTaskbarBtnCreated)
97 END_MESSAGE_MAP()
99 BOOL CProgressDlg::OnInitDialog()
101 CResizableStandAloneDialog::OnInitDialog();
103 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
104 // do this, Explorer would be unable to send that message to our window if we
105 // were running elevated. It's OK to make the call all the time, since if we're
106 // not elevated, this is a no-op.
107 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
108 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
109 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(_T("user32.dll"));
110 if (hUser)
112 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
113 if (pfnChangeWindowMessageFilterEx)
115 pfnChangeWindowMessageFilterEx(m_hWnd, WM_TASKBARBTNCREATED, MSGFLT_ALLOW, &cfs);
118 m_pTaskbarList.Release();
119 if (FAILED(m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
120 m_pTaskbarList = nullptr;
122 AddAnchor(IDC_TITLE_ANIMATE, TOP_LEFT, TOP_RIGHT);
123 AddAnchor(IDC_RUN_PROGRESS, TOP_LEFT,TOP_RIGHT);
124 AddAnchor(IDC_LOG, TOP_LEFT,BOTTOM_RIGHT);
126 AddAnchor(IDOK,BOTTOM_RIGHT);
127 AddAnchor(IDCANCEL,BOTTOM_RIGHT);
128 AddAnchor(IDC_PROGRESS_BUTTON1,BOTTOM_LEFT);
129 AddAnchor(IDC_CURRENT,TOP_LEFT);
131 this->GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_HIDE);
132 m_Animate.Open(IDR_DOWNLOAD);
134 CFont m_logFont;
135 CAppUtils::CreateFontForLogs(m_logFont);
136 //GetDlgItem(IDC_CMD_LOG)->SetFont(&m_logFont);
137 m_Log.SetFont(&m_logFont);
139 CString InitialText;
140 if ( !m_PreText.IsEmpty() )
142 InitialText = m_PreText + _T("\r\n");
144 #if 0
145 if (m_bShowCommand && (!m_GitCmd.IsEmpty() ))
147 InitialText += m_GitCmd+_T("\r\n\r\n");
149 #endif
150 m_Log.SetWindowTextW(InitialText);
151 m_CurrentWork.SetWindowTextW(_T(""));
153 EnableSaveRestore(_T("ProgressDlg"));
155 m_pThread = AfxBeginThread(ProgressThreadEntry, this, THREAD_PRIORITY_NORMAL,0,CREATE_SUSPENDED);
156 if (m_pThread==NULL)
158 CMessageBox::Show(this->m_hWnd, IDS_ERR_THREADSTARTFAILED, IDS_APPNAME, MB_OK | MB_ICONERROR);
159 DialogEnableWindow(IDCANCEL, TRUE);
161 else
163 m_pThread->m_bAutoDelete = FALSE;
164 m_pThread->ResumeThread();
167 CString sWindowTitle;
168 GetWindowText(sWindowTitle);
169 CAppUtils::SetWindowTitle(m_hWnd, m_Git->m_CurrentDir, sWindowTitle);
171 // Make sure this dialog is shown in foreground (see issue #1536)
172 SetForegroundWindow();
174 return TRUE;
177 static void EnsurePostMessage(CWnd *pWnd, UINT Msg, WPARAM wParam, LPARAM lParam)
179 redo:
180 if (!pWnd->PostMessage(Msg, wParam, lParam))
182 if (GetLastError() == ERROR_NOT_ENOUGH_QUOTA)
184 Sleep(20);
185 goto redo;
187 else
188 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": Message %d-%d could not be sent (error %d; %s)\n"), wParam, lParam, GetLastError(), (CString)CFormatMessageWrapper());
192 UINT CProgressDlg::ProgressThreadEntry(LPVOID pVoid)
194 return ((CProgressDlg*)pVoid)->ProgressThread();
197 //static function, Share with SyncDialog
198 UINT CProgressDlg::RunCmdList(CWnd* pWnd, STRING_VECTOR& cmdlist, STRING_VECTOR& dirlist, bool bShowCommand, CString* pfilename, bool* bAbort, CGitGuardedByteArray* pdata, CGit* git)
200 UINT ret=0;
202 std::vector<std::unique_ptr<CBlockCacheForPath>> cacheBlockList;
203 std::vector<std::unique_ptr<CGit>> gitList;
204 if (dirlist.empty())
205 cacheBlockList.push_back(std::unique_ptr<CBlockCacheForPath>(new CBlockCacheForPath(git->m_CurrentDir)));
206 else
208 for (auto dir : dirlist)
210 CGit *pGit = new CGit;
211 pGit->m_CurrentDir = dir;
212 gitList.push_back(std::unique_ptr<CGit>(pGit));
213 cacheBlockList.push_back(std::unique_ptr<CBlockCacheForPath>(new CBlockCacheForPath(dir)));
217 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_START, 0);
219 if(pdata)
220 pdata->clear();
222 for (size_t i = 0; i < cmdlist.size(); ++i)
224 if(cmdlist[i].IsEmpty())
225 continue;
227 if (bShowCommand)
229 CStringA str;
230 if (gitList.empty() || gitList.size() == 1 && gitList[0]->m_CurrentDir == git->m_CurrentDir)
231 str = CUnicodeUtils::GetMulti(cmdlist[i].Trim() + _T("\r\n\r\n"), CP_UTF8);
232 else
233 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);
234 for (int j = 0; j < str.GetLength(); ++j)
236 if(pdata)
238 pdata->m_critSec.Lock();
239 pdata->push_back(str[j]);
240 pdata->m_critSec.Unlock();
242 else
243 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,str[j]);
245 if(pdata)
246 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
249 PROCESS_INFORMATION pi;
250 CAutoGeneralHandle hRead;
251 int runAsyncRet = -1;
252 if (gitList.empty())
253 runAsyncRet = git->RunAsync(cmdlist[i].Trim(), &pi, hRead.GetPointer(), nullptr, pfilename);
254 else
255 runAsyncRet = gitList[i]->RunAsync(cmdlist[i].Trim(), &pi, hRead.GetPointer(), nullptr, pfilename);
256 if (runAsyncRet)
258 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_FAILED, -1 * runAsyncRet);
259 return runAsyncRet;
262 CAutoGeneralHandle piProcess(pi.hProcess);
263 CAutoGeneralHandle piThread(pi.hThread);
264 DWORD readnumber;
265 char lastByte = '\0';
266 char byte;
267 CString output;
268 while(ReadFile(hRead,&byte,1,&readnumber,NULL))
270 if(pdata)
272 if(byte == 0)
273 byte = '\n';
275 pdata->m_critSec.Lock();
276 if (byte == '\n' && lastByte != '\r')
277 pdata->push_back('\r');
278 pdata->push_back( byte);
279 lastByte = byte;
280 pdata->m_critSec.Unlock();
282 if(byte == '\r' || byte == '\n')
283 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,0);
285 else
286 pWnd->PostMessage(MSG_PROGRESSDLG_UPDATE_UI,MSG_PROGRESSDLG_RUN,byte);
288 if (pdata)
290 pdata->m_critSec.Lock();
291 bool post = !pdata->empty();
292 pdata->m_critSec.Unlock();
293 if (post)
294 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_RUN, 0);
297 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": waiting for process to finish (%s), aborted: %d\n"), cmdlist[i], *bAbort);
299 WaitForSingleObject(pi.hProcess, INFINITE);
301 DWORD status=0;
302 if(!GetExitCodeProcess(pi.hProcess,&status) || *bAbort)
304 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": process %s finished, status code could not be fetched, (error %d; %s), aborted: %d\n"), cmdlist[i], GetLastError(), (CString)CFormatMessageWrapper(), *bAbort);
306 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_FAILED, status);
307 return TGIT_GIT_ERROR_GET_EXIT_CODE;
309 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": process %s finished with code %d\n"), cmdlist[i], status);
310 ret |= status;
313 EnsurePostMessage(pWnd, MSG_PROGRESSDLG_UPDATE_UI, MSG_PROGRESSDLG_END, ret);
315 return ret;
318 UINT CProgressDlg::ProgressThread()
321 m_GitCmdList.push_back(m_GitCmd);
323 CString *pfilename;
325 if(m_LogFile.IsEmpty())
326 pfilename=NULL;
327 else
328 pfilename=&m_LogFile;
330 m_startTick = GetTickCount();
331 m_GitStatus = RunCmdList(this, m_GitCmdList, m_GitDirList, m_bShowCommand, pfilename, &m_bAbort, &this->m_Databuf, m_Git);
332 return 0;
335 LRESULT CProgressDlg::OnProgressUpdateUI(WPARAM wParam,LPARAM lParam)
337 if(wParam == MSG_PROGRESSDLG_START)
339 m_BufStart = 0 ;
340 m_Animate.Play(0, INT_MAX, INT_MAX);
341 DialogEnableWindow(IDCANCEL, TRUE);
342 if (m_pTaskbarList)
344 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
345 m_pTaskbarList->SetProgressValue(m_hWnd, 0, 100);
348 if(wParam == MSG_PROGRESSDLG_END || wParam == MSG_PROGRESSDLG_FAILED)
350 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": got message: %d\n"), wParam);
351 DWORD tickSpent = GetTickCount() - m_startTick;
352 CString strEndTime = CLoglistUtils::FormatDateAndTime(CTime::GetCurrentTime(), DATE_SHORTDATE, true, false);
354 if(m_bBufferAll)
356 m_Databuf.m_critSec.Lock();
357 m_Databuf.push_back(0);
358 m_Log.SetWindowText(Convert2UnionCode((char*)&m_Databuf[0]));
359 m_Databuf.m_critSec.Unlock();
360 m_Log.LineScroll(m_Log.GetLineCount() - m_Log.GetFirstVisibleLine() - 4);
362 m_BufStart=0;
363 m_Databuf.m_critSec.Lock();
364 this->m_Databuf.clear();
365 m_Databuf.m_critSec.Unlock();
367 m_bDone = true;
368 m_Animate.Stop();
369 m_Progress.SetPos(100);
370 this->DialogEnableWindow(IDOK,TRUE);
372 m_GitStatus = (DWORD)lParam;
374 // detect crashes of perl when performing git svn actions
375 if (m_GitStatus == 0 && m_GitCmd.Find(_T(" svn ")) > 1)
377 CString log;
378 m_Log.GetWindowText(log);
379 if (log.GetLength() > 18 && log.Mid(log.GetLength() - 18) == _T("perl.exe.stackdump"))
380 m_GitStatus = (DWORD)-1;
383 if(this->m_GitStatus)
385 if (m_pTaskbarList)
387 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_ERROR);
388 m_pTaskbarList->SetProgressValue(m_hWnd, 100, 100);
390 CString log;
391 log.Format(IDS_PROC_PROGRESS_GITUNCLEANEXIT, m_GitStatus);
392 CString err;
393 if (CRegDWORD(_T("Software\\TortoiseGit\\ShowGitexeTimings"), TRUE))
394 err.Format(_T("\r\n\r\n%s (%lu ms @ %s)\r\n"), log, tickSpent, strEndTime);
395 else
396 err.Format(_T("\r\n\r\n%s\r\n"), log);
397 InsertColorText(this->m_Log, err, RGB(255,0,0));
398 if (CRegDWORD(_T("Software\\TortoiseGit\\NoSounds"), FALSE) == FALSE)
399 PlaySound((LPCTSTR)SND_ALIAS_SYSTEMEXCLAMATION, NULL, SND_ALIAS_ID | SND_ASYNC);
401 else {
402 if (m_pTaskbarList)
403 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NOPROGRESS);
404 CString temp;
405 temp.LoadString(IDS_SUCCESS);
406 CString log;
407 if (CRegDWORD(_T("Software\\TortoiseGit\\ShowGitexeTimings"), TRUE))
408 log.Format(_T("\r\n%s (%lu ms @ %s)\r\n"), temp, tickSpent, strEndTime);
409 else
410 log.Format(_T("\r\n%s\r\n"), temp);
411 InsertColorText(this->m_Log, log, RGB(0,0,255));
412 this->DialogEnableWindow(IDCANCEL,FALSE);
415 if (wParam == MSG_PROGRESSDLG_END)
417 if (m_PostCmdCallback) // new handling method using callback
419 m_PostCmdCallback(m_GitStatus, m_PostCmdList);
421 if (!m_PostCmdList.empty())
423 for (auto it = m_PostCmdList.cbegin(); it != m_PostCmdList.cend(); ++it)
424 m_ctrlPostCmd.AddEntry((*it).icon, (*it).label);
425 GetDlgItem(IDC_PROGRESS_BUTTON1)->ShowWindow(SW_SHOW);
430 if(wParam == MSG_PROGRESSDLG_END && m_GitStatus == 0)
432 if (m_AutoClose == AUTOCLOSE_IF_NO_OPTIONS && m_PostCmdList.empty() || m_AutoClose == AUTOCLOSE_IF_NO_ERRORS)
433 PostMessage(WM_COMMAND, 1, (LPARAM)GetDlgItem(IDOK)->m_hWnd);
437 if(!m_bBufferAll)
439 if(lParam == 0)
441 m_Databuf.m_critSec.Lock();
442 for (size_t i = this->m_BufStart; i < this->m_Databuf.size(); ++i)
444 char c = this->m_Databuf[m_BufStart];
445 ++m_BufStart;
446 m_Databuf.m_critSec.Unlock();
447 ParserCmdOutput(c);
449 m_Databuf.m_critSec.Lock();
452 if(m_BufStart>1000)
454 m_Databuf.erase(m_Databuf.begin(), m_Databuf.begin()+m_BufStart);
455 m_BufStart =0;
457 m_Databuf.m_critSec.Unlock();
460 else
461 ParserCmdOutput((char)lParam);
463 return 0;
466 //static function, Share with SyncDialog
467 int CProgressDlg::FindPercentage(CString &log)
469 int s1=log.Find(_T('%'));
470 if(s1<0)
471 return -1;
473 int s2=s1-1;
474 for(int i=s1-1;i>=0;i--)
476 if(log[i]>=_T('0') && log[i]<=_T('9'))
477 s2=i;
478 else
479 break;
481 return _ttol(log.Mid(s2,s1-s2));
484 void CProgressDlg::ParserCmdOutput(char ch)
486 ParserCmdOutput(this->m_Log,this->m_Progress,this->m_hWnd,this->m_pTaskbarList,this->m_LogTextA,ch,&this->m_CurrentWork);
488 void CProgressDlg::ClearESC(CString &str)
490 // see http://ascii-table.com/ansi-escape-sequences.php and http://tldp.org/HOWTO/Bash-Prompt-HOWTO/c327.html
491 str.Replace(_T("\033[K"), _T("")); // erase until end of line; no need to care for this, because we always clear the whole line
493 // drop colors
494 while (true)
496 int escapePosition = str.Find(_T('\033'));
497 if (escapePosition >= 0 && str.GetLength() >= escapePosition + 3)
499 if (str.Mid(escapePosition, 2) == _T("\033["))
501 int colorEnd = str.Find(_T('m'), escapePosition + 2);
502 if (colorEnd > 0)
504 bool found = true;
505 for (int i = escapePosition + 2; i < colorEnd; ++i)
507 if (str[i] != _T(';') && (str[i] < _T('0') && str[i] > _T('9')))
509 found = false;
510 break;
513 if (found)
515 if (escapePosition > 0)
516 str = str.Left(escapePosition) + str.Mid(colorEnd + 1);
517 else
518 str = str.Mid(colorEnd);
519 continue;
524 break;
527 void CProgressDlg::ParserCmdOutput(CRichEditCtrl &log,CProgressCtrl &progressctrl,HWND m_hWnd,CComPtr<ITaskbarList3> m_pTaskbarList,CStringA &oneline, char ch, CWnd *CurrentWork)
529 //TRACE(_T("%c"),ch);
530 if( ch == ('\r') || ch == ('\n'))
532 CString str = CUnicodeUtils::GetUnicode(oneline);
534 // TRACE(_T("End Char %s \r\n"),ch==_T('\r')?_T("lf"):_T(""));
535 // TRACE(_T("End Char %s \r\n"),ch==_T('\n')?_T("cr"):_T(""));
537 int lines = log.GetLineCount();
538 str.Trim();
539 // TRACE(_T("%s"), str);
541 ClearESC(str);
543 if(ch == ('\r'))
545 int start=log.LineIndex(lines-1);
546 log.SetSel(start, log.GetTextLength());
547 log.ReplaceSel(str);
549 else
551 int length = log.GetWindowTextLength();
552 log.SetSel(length, length);
553 if (length > 0)
554 log.ReplaceSel(_T("\r\n") + str);
555 else
556 log.ReplaceSel(str);
559 if (lines > 500) //limited log length
561 int end=log.LineIndex(1);
562 log.SetSel(0,end);
563 log.ReplaceSel(_T(""));
565 log.LineScroll(log.GetLineCount() - log.GetFirstVisibleLine() - 4);
567 int s1=oneline.ReverseFind(_T(':'));
568 int s2=oneline.Find(_T('%'));
569 if (s1 > 0 && s2 > 0)
571 if(CurrentWork)
572 CurrentWork->SetWindowTextW(str.Left(s1));
574 int pos=FindPercentage(str);
575 TRACE(_T("Pos %d\r\n"),pos);
576 if(pos>0)
578 progressctrl.SetPos(pos);
579 if (m_pTaskbarList)
581 m_pTaskbarList->SetProgressState(m_hWnd, TBPF_NORMAL);
582 m_pTaskbarList->SetProgressValue(m_hWnd, pos, 100);
587 oneline="";
590 else
592 oneline+=ch;
595 void CProgressDlg::RemoveLastLine(CString &str)
597 int start;
598 start=str.ReverseFind(_T('\n'));
599 if(start>0)
600 str=str.Left(start);
601 return;
603 // CProgressDlg message handlers
605 void CProgressDlg::WriteLog() const
607 CLogFile logfile(g_Git.m_CurrentDir);
608 if (logfile.Open())
610 logfile.AddTimeLine();
611 CString text = GetLogText();
612 LPTSTR psz_string = text.GetBuffer();
613 while (*psz_string)
615 if (*psz_string == '\r')
617 ++psz_string;
618 continue;
620 size_t i_len = wcscspn(psz_string, L"\n");
621 logfile.AddLine(CString(psz_string, (int)i_len));
622 psz_string += i_len;
623 if (*psz_string == '\n')
624 ++psz_string;
626 if (m_bAbort)
628 CString canceled;
629 canceled.LoadString(IDS_SVN_USERCANCELLED);
630 logfile.AddLine(canceled);
632 logfile.Close();
636 void CProgressDlg::OnBnClickedOk()
638 if (m_pThread) // added here because Close-button is "called" from thread by PostMessage
639 ::WaitForSingleObject(m_pThread->m_hThread, 5000);
640 m_Log.GetWindowText(this->m_LogText);
641 WriteLog();
642 OnOK();
645 void CProgressDlg::OnBnClickedButton1()
647 WriteLog();
648 ShowWindow(SW_HIDE);
649 m_PostCmdList.at(m_ctrlPostCmd.GetCurrentEntry()).action();
650 EndDialog(IDOK);
653 void CProgressDlg::OnClose()
655 DialogEnableWindow(IDCANCEL, TRUE);
656 __super::OnClose();
659 void CProgressDlg::OnCancel()
661 CTraceToOutputDebugString::Instance()(_T(__FUNCTION__) _T(": User canceled\n"));
662 m_bAbort = true;
663 if(m_bDone)
665 WriteLog();
666 CResizableStandAloneDialog::OnCancel();
667 return;
670 if( m_Git->m_CurrentGitPi.hProcess )
672 DWORD dwConfirmKillProcess = CRegDWORD(_T("Software\\TortoiseGit\\ConfirmKillProcess"));
673 if (dwConfirmKillProcess && CMessageBox::Show(m_hWnd, IDS_PROC_CONFIRMKILLPROCESS, IDS_APPNAME, MB_YESNO | MB_ICONQUESTION) != IDYES)
674 return;
675 if(::GenerateConsoleCtrlEvent(CTRL_C_EVENT,0))
677 ::WaitForSingleObject(m_Git->m_CurrentGitPi.hProcess ,10000);
680 KillProcessTree(m_Git->m_CurrentGitPi.dwProcessId);
683 ::WaitForSingleObject(m_Git->m_CurrentGitPi.hProcess ,10000);
684 if (m_pThread)
685 ::WaitForSingleObject(m_pThread->m_hThread, 5000);
686 WriteLog();
687 CResizableStandAloneDialog::OnCancel();
690 void CProgressDlg::KillProcessTree(DWORD dwProcessId, unsigned int depth)
692 // recursively kills a process tree
693 // This is not optimized, but works and isn't called very often ;)
695 if (!dwProcessId || depth > 20)
696 return;
698 PROCESSENTRY32 pe;
699 memset(&pe, 0, sizeof(PROCESSENTRY32));
700 pe.dwSize = sizeof(PROCESSENTRY32);
702 CAutoGeneralHandle hSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
704 if (::Process32First(hSnap, &pe))
708 if (pe.th32ParentProcessID == dwProcessId)
709 KillProcessTree(pe.th32ProcessID, depth + 1);
710 } while (::Process32Next(hSnap, &pe));
712 CAutoGeneralHandle hProc = ::OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
713 if (hProc)
714 ::TerminateProcess(hProc, 1);
718 void CProgressDlg::InsertColorText(CRichEditCtrl &edit,CString text,COLORREF rgb)
720 CHARFORMAT old,cf;
721 edit.GetDefaultCharFormat(cf);
722 old=cf;
723 cf.dwMask|=CFM_COLOR;
724 cf.crTextColor=rgb;
725 cf.dwEffects|=CFE_BOLD;
726 cf.dwEffects &= ~CFE_AUTOCOLOR ;
727 edit.SetSel(edit.GetTextLength()-1,edit.GetTextLength());
728 edit.ReplaceSel(text);
729 edit.SetSel(edit.LineIndex(edit.GetLineCount()-2),edit.GetTextLength());
730 edit.SetSelectionCharFormat(cf);
731 edit.SetSel(edit.GetTextLength(),edit.GetTextLength());
732 edit.SetDefaultCharFormat(old);
733 edit.LineScroll(edit.GetLineCount() - edit.GetFirstVisibleLine() - 4);
736 CString CCommitProgressDlg::Convert2UnionCode(char *buff, int size)
738 int start=0;
739 if(size == -1)
740 size = (int)strlen(buff);
742 for (int i = 0; i < size; ++i)
744 if(buff[i] == ']')
745 start = i;
746 if( start >0 && buff[i] =='\n' )
748 start =i;
749 break;
753 CString str;
754 CGit::StringAppend(&str, (BYTE*)buff, g_Git.m_LogEncode, start);
755 CGit::StringAppend(&str, (BYTE*)buff + start, CP_UTF8, size - start);
757 ClearESC(str);
759 return str;
762 LRESULT CProgressDlg::OnTaskbarBtnCreated(WPARAM /*wParam*/, LPARAM /*lParam*/)
764 m_pTaskbarList.Release();
765 m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
766 SetUUIDOverlayIcon(m_hWnd);
767 return 0;
770 BOOL CProgressDlg::PreTranslateMessage(MSG* pMsg)
772 if (pMsg->message == WM_KEYDOWN)
774 if (pMsg->wParam == VK_ESCAPE)
776 // pressing the ESC key should close the dialog. But since we disabled the escape
777 // key (so the user doesn't get the idea that he could simply undo an e.g. update)
778 // this won't work.
779 // So if the user presses the ESC key, change it to VK_RETURN so the dialog gets
780 // the impression that the OK button was pressed.
781 if ((!GetDlgItem(IDCANCEL)->IsWindowEnabled())
782 &&(GetDlgItem(IDOK)->IsWindowEnabled())&&(GetDlgItem(IDOK)->IsWindowVisible()))
784 // since we convert ESC to RETURN, make sure the OK button has the focus.
785 GetDlgItem(IDOK)->SetFocus();
786 pMsg->wParam = VK_RETURN;
790 else if (pMsg->message == WM_CONTEXTMENU || pMsg->message == WM_RBUTTONDOWN)
792 CWnd * pWnd = (CWnd*) GetDlgItem(IDC_LOG);
793 if (pWnd == GetFocus())
795 CIconMenu popup;
796 if (popup.CreatePopupMenu())
798 long start = -1, end = -1;
799 auto pEdit = (CRichEditCtrl *)GetDlgItem(IDC_LOG);
800 pEdit->GetSel(start, end);
801 popup.AppendMenuIcon(WM_COPY, IDS_SCIEDIT_COPY, IDI_COPYCLIP);
802 if (start >= end)
803 popup.EnableMenuItem(WM_COPY, MF_BYCOMMAND | MF_DISABLED | MF_GRAYED);
804 popup.AppendMenu(MF_SEPARATOR);
805 popup.AppendMenuIcon(EM_SETSEL, IDS_STATUSLIST_CONTEXT_COPYEXT, IDI_COPYCLIP);
806 int cmd = popup.TrackPopupMenu(TPM_RETURNCMD | TPM_LEFTALIGN | TPM_NONOTIFY, pMsg->pt.x, pMsg->pt.y, this);
807 switch (cmd)
809 case 0: // no command selected
810 break;
811 case EM_SETSEL:
813 pEdit->SetRedraw(FALSE);
814 int oldLine = pEdit->GetFirstVisibleLine();
815 pEdit->SetSel(0, -1);
816 pEdit->Copy();
817 pEdit->SetSel(start, end);
818 int newLine = pEdit->GetFirstVisibleLine();
819 pEdit->LineScroll(oldLine - newLine);
820 pEdit->SetRedraw(TRUE);
821 pEdit->RedrawWindow();
823 break;
824 case WM_COPY:
825 ::SendMessage(GetDlgItem(IDC_LOG)->GetSafeHwnd(), cmd, 0, -1);
826 break;
828 return TRUE;
832 return __super::PreTranslateMessage(pMsg);