Fixed issue #4141: Cannot remove remote: "usage: git remote remove <name>"
[TortoiseGit.git] / src / Utils / MiscUI / SysProgressDlg.cpp
blobe211299daca3bdf5cc35cdc71abebc21bc26b97a
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2009-2013, 2016-2017, 2023 - TortoiseGit
4 // Copyright (C) 2003-2006,2008-2011,2015 - TortoiseSVN
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License
8 // as published by the Free Software Foundation; either version 2
9 // of the License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software Foundation,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 #include "stdafx.h"
21 #include "SysProgressDlg.h"
23 CSysProgressDlg::CSysProgressDlg()
25 EnsureValid();
28 CSysProgressDlg::~CSysProgressDlg()
30 if (IsValid())
32 if (m_isVisible) //still visible, so stop first before destroying
33 m_pIDlg->StopProgressDialog();
35 m_pIDlg.Release();
36 m_hWndProgDlg = nullptr;
40 bool CSysProgressDlg::EnsureValid()
42 if(IsValid())
43 return true;
45 HRESULT hr = m_pIDlg.CoCreateInstance(CLSID_ProgressDialog, nullptr, CLSCTX_INPROC_SERVER);
46 return (SUCCEEDED(hr));
49 void CSysProgressDlg::SetTitle(LPCWSTR szTitle)
51 USES_CONVERSION;
52 if (IsValid())
53 m_pIDlg->SetTitle(T2COLE(szTitle));
55 void CSysProgressDlg::SetTitle ( UINT idTitle)
57 SetTitle(CString(MAKEINTRESOURCE(idTitle)));
60 void CSysProgressDlg::SetLine(DWORD dwLine, LPCWSTR szText, bool bCompactPath /* = false */)
62 USES_CONVERSION;
63 if (IsValid())
64 m_pIDlg->SetLine(dwLine, T2COLE(szText), bCompactPath, nullptr);
67 #ifdef _MFC_VER
68 void CSysProgressDlg::SetCancelMsg ( UINT idMessage )
70 SetCancelMsg(CString(MAKEINTRESOURCE(idMessage)));
72 #endif // _MFC_VER
74 void CSysProgressDlg::SetCancelMsg(LPCWSTR szMessage)
76 USES_CONVERSION;
77 if (IsValid())
78 m_pIDlg->SetCancelMsg(T2COLE(szMessage), nullptr);
81 void CSysProgressDlg::SetTime(bool bTime /* = true */)
83 m_dwDlgFlags &= ~(PROGDLG_NOTIME | PROGDLG_AUTOTIME);
85 if (bTime)
86 m_dwDlgFlags |= PROGDLG_AUTOTIME;
87 else
88 m_dwDlgFlags |= PROGDLG_NOTIME;
91 void CSysProgressDlg::SetShowProgressBar(bool bShow /* = true */)
93 if (bShow)
94 m_dwDlgFlags &= ~PROGDLG_NOPROGRESSBAR;
95 else
96 m_dwDlgFlags |= PROGDLG_NOPROGRESSBAR;
98 #ifdef _MFC_VER
99 HRESULT CSysProgressDlg::ShowModal (CWnd* pwndParent, BOOL immediately /* = true */)
101 EnsureValid();
102 return ShowModal(pwndParent->GetSafeHwnd(), immediately);
105 HRESULT CSysProgressDlg::ShowModeless(CWnd* pwndParent, BOOL immediately)
107 EnsureValid();
108 return ShowModeless(pwndParent->GetSafeHwnd(), immediately);
111 void CSysProgressDlg::FormatPathLine ( DWORD dwLine, UINT idFormatText, ...)
113 va_list args;
114 va_start(args, idFormatText);
116 CString sText;
117 sText.FormatV(CString(MAKEINTRESOURCE(idFormatText)), args);
118 SetLine(dwLine, sText, true);
120 va_end(args);
123 void CSysProgressDlg::FormatPathLine(DWORD dwLine, LPCWSTR FormatText, ...)
125 va_list args;
126 va_start(args, FormatText);
128 CString sText;
129 sText.FormatV(FormatText, args);
130 SetLine(dwLine, sText, true);
132 va_end(args);
135 void CSysProgressDlg::FormatNonPathLine(DWORD dwLine, UINT idFormatText, ...)
137 va_list args;
138 va_start(args, idFormatText);
140 CString sText;
141 sText.FormatV(CString(MAKEINTRESOURCE(idFormatText)), args);
142 SetLine(dwLine, sText, false);
144 va_end(args);
147 void CSysProgressDlg::FormatNonPathLine(DWORD dwLine, LPCWSTR FormatText, ...)
149 va_list args;
150 va_start(args, FormatText);
152 CString sText;
153 sText.FormatV(FormatText, args);
154 SetLine(dwLine, sText, false);
156 va_end(args);
159 #endif
160 HRESULT CSysProgressDlg::ShowModal(HWND hWndParent, BOOL immediately /* = true */)
162 EnsureValid();
163 m_hWndProgDlg = nullptr;
164 if (!IsValid())
165 return E_FAIL;
166 m_hWndParent = hWndParent;
167 auto winId = GetWindowThreadProcessId(m_hWndParent, nullptr);
168 auto threadId = GetCurrentThreadId();
169 if (winId != threadId)
170 AttachThreadInput(winId, threadId, TRUE);
171 m_hWndFocus = GetFocus();
172 if (winId != threadId)
173 AttachThreadInput(winId, threadId, FALSE);
174 HRESULT hr = m_pIDlg->StartProgressDialog(hWndParent, nullptr, m_dwDlgFlags | PROGDLG_MODAL, nullptr);
175 if(FAILED(hr))
176 return hr;
178 ATL::CComPtr<IOleWindow> pOleWindow;
179 HRESULT hr2 = m_pIDlg.QueryInterface(&pOleWindow);
180 if(SUCCEEDED(hr2))
182 hr2 = pOleWindow->GetWindow(&m_hWndProgDlg);
183 if(SUCCEEDED(hr2))
185 if(immediately)
186 ShowWindow(m_hWndProgDlg, SW_SHOW);
190 m_isVisible = true;
191 return hr;
194 HRESULT CSysProgressDlg::ShowModeless(HWND hWndParent, BOOL immediately)
196 EnsureValid();
197 m_hWndProgDlg = nullptr;
198 if (!IsValid())
199 return E_FAIL;
200 m_hWndParent = hWndParent;
201 auto winId = GetWindowThreadProcessId(m_hWndParent, nullptr);
202 auto threadId = GetCurrentThreadId();
203 if (winId != threadId)
204 AttachThreadInput(winId, threadId, TRUE);
205 m_hWndFocus = GetFocus();
206 if (winId != threadId)
207 AttachThreadInput(winId, threadId, FALSE);
208 HRESULT hr = m_pIDlg->StartProgressDialog(hWndParent, nullptr, m_dwDlgFlags, nullptr);
209 if(FAILED(hr))
210 return hr;
212 ATL::CComPtr<IOleWindow> pOleWindow;
213 HRESULT hr2 = m_pIDlg.QueryInterface(&pOleWindow);
214 if(SUCCEEDED(hr2))
216 hr2 = pOleWindow->GetWindow(&m_hWndProgDlg);
217 if(SUCCEEDED(hr2))
219 if (immediately)
220 ShowWindow(m_hWndProgDlg, SW_SHOW);
223 m_isVisible = true;
224 return hr;
227 void CSysProgressDlg::SetProgress(DWORD dwProgress, DWORD dwMax)
229 if (IsValid())
230 m_pIDlg->SetProgress(dwProgress, dwMax);
233 void CSysProgressDlg::SetProgress64(ULONGLONG u64Progress, ULONGLONG u64ProgressMax)
235 if (IsValid())
236 m_pIDlg->SetProgress64(u64Progress, u64ProgressMax);
239 bool CSysProgressDlg::HasUserCancelled()
241 if (!IsValid())
242 return false;
244 return (0 != m_pIDlg->HasUserCancelled());
247 void CSysProgressDlg::Stop()
249 if ((m_isVisible)&&(IsValid()))
251 m_pIDlg->StopProgressDialog();
252 // Sometimes the progress dialog sticks around after stopping it,
253 // until the mouse pointer is moved over it or some other triggers.
254 // We hide the window here immediately.
255 if (m_hWndProgDlg)
257 // The progress dialog is handled on a separate thread, which means
258 // even calling StopProgressDialog() will not stop it immediately.
259 // Even destroying the progress window is not enough.
260 // Which can cause problems with modality: if we stop the progress
261 // dialog and immediately show e.g. a messagebox over the same
262 // window the progress dialog has as its parent, then the messagebox
263 // is modal first (deactivates the parent), but then a little bit
264 // later the progress dialog finally closes properly, and by doing that
265 // re-enables its parent window.
266 // Which leads to the parent window being enabled even though
267 // the modal messagebox is shown over the parent window.
268 // This situation can even lead to the messagebox appearing *behind*
269 // the parent window (race condition)
271 // So, to really ensure that the progress dialog is fully stopped
272 // and destroyed, we have to attach to its UI thread and handle
273 // all messages until there are no more messages: that's when
274 // the progress dialog is really gone.
275 AttachThreadInput(GetWindowThreadProcessId(m_hWndProgDlg, 0), GetCurrentThreadId(), TRUE);
276 // StartProgressDialog creates a new thread to host the progress window.
277 // When the window receives WM_DESTROY message StopProgressDialog() wrongly
278 // attempts to re-enable the parent in the calling thread (our thread),
279 // after the progress window is destroyed and the progress thread has died.
280 // When the progress window dies, the system tries to assign a new foreground window.
281 // It cannot assign to hwndParent because StartProgressDialog (w/PROGDLG_MODAL) disabled the parent window.
282 // So the system hands the foreground activation to the next process that wants it in the
283 // system foreground queue. Thus we lose our right to recapture the foreground window.
284 // To fix this problem, we enable the parent window and set to focus to it here, after
285 // we've attached to the window thread.
286 ShowWindow(m_hWndProgDlg, SW_HIDE);
287 EnableWindow(m_hWndParent, TRUE);
288 if (m_hWndFocus)
289 SetFocus(m_hWndFocus);
290 else
291 SetFocus(m_hWndParent);
292 auto start = GetTickCount64();
293 while (::IsWindow(m_hWndProgDlg) && ((GetTickCount64() - start) < 3000))
295 MSG msg = { 0 };
296 while (PeekMessage(&msg, m_hWndProgDlg, 0, 0, PM_REMOVE))
301 m_isVisible = false;
302 m_pIDlg.Release();
304 m_hWndProgDlg = nullptr;
308 void CSysProgressDlg::ResetTimer()
310 if (IsValid())
311 m_pIDlg->Timer(PDTIMER_RESET, nullptr);