Provide (experimental) clang-format file
[TortoiseGit.git] / src / TortoiseProc / GitProgressDlg.cpp
blob2683e0bd586ebdcc18b746413b1ec27750c0d536
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2018 - TortoiseGit
4 // Copyright (C) 2003-2008 - 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 "TortoiseProc.h"
22 #include "GitProgressDlg.h"
23 #include "AppUtils.h"
24 #include "SmartHandle.h"
25 #include "StringUtils.h"
26 #include "CmdLineParser.h"
28 IMPLEMENT_DYNAMIC(CGitProgressDlg, CResizableStandAloneDialog)
29 CGitProgressDlg::CGitProgressDlg(CWnd* pParent /*=nullptr*/)
30 : CResizableStandAloneDialog(CGitProgressDlg::IDD, pParent)
31 , m_AutoClose(AUTOCLOSE_NO)
32 , m_hAccel(nullptr)
36 CGitProgressDlg::~CGitProgressDlg()
38 if (m_hAccel)
39 DestroyAcceleratorTable(m_hAccel);
42 void CGitProgressDlg::DoDataExchange(CDataExchange* pDX)
44 CResizableStandAloneDialog::DoDataExchange(pDX);
45 DDX_Control(pDX, IDC_SVNPROGRESS, m_ProgList);
46 DDX_Control(pDX, IDC_TITLE_ANIMATE, m_Animate);
47 DDX_Control(pDX, IDC_PROGRESSBAR, m_ProgCtrl);
48 DDX_Control(pDX, IDC_INFOTEXT, m_InfoCtrl);
49 DDX_Control(pDX, IDC_PROGRESSLABEL, m_ProgLableCtrl);
50 DDX_Control(pDX, IDC_LOGBUTTON, m_cMenuButton);
53 BEGIN_MESSAGE_MAP(CGitProgressDlg, CResizableStandAloneDialog)
54 ON_BN_CLICKED(IDC_LOGBUTTON, OnBnClickedLogbutton)
55 ON_WM_CLOSE()
56 ON_WM_SETCURSOR()
57 ON_EN_SETFOCUS(IDC_INFOTEXT, &CGitProgressDlg::OnEnSetfocusInfotext)
58 ON_WM_CTLCOLOR()
59 ON_MESSAGE(WM_PROG_CMD_FINISH, OnCmdEnd)
60 ON_MESSAGE(WM_PROG_CMD_START, OnCmdStart)
61 ON_REGISTERED_MESSAGE(TaskBarButtonCreated, OnTaskbarBtnCreated)
62 END_MESSAGE_MAP()
66 BOOL CGitProgressDlg::OnInitDialog()
68 __super::OnInitDialog();
70 // Let the TaskbarButtonCreated message through the UIPI filter. If we don't
71 // do this, Explorer would be unable to send that message to our window if we
72 // were running elevated. It's OK to make the call all the time, since if we're
73 // not elevated, this is a no-op.
74 CHANGEFILTERSTRUCT cfs = { sizeof(CHANGEFILTERSTRUCT) };
75 typedef BOOL STDAPICALLTYPE ChangeWindowMessageFilterExDFN(HWND hWnd, UINT message, DWORD action, PCHANGEFILTERSTRUCT pChangeFilterStruct);
76 CAutoLibrary hUser = AtlLoadSystemLibraryUsingFullPath(L"user32.dll");
77 if (hUser)
79 ChangeWindowMessageFilterExDFN *pfnChangeWindowMessageFilterEx = (ChangeWindowMessageFilterExDFN*)GetProcAddress(hUser, "ChangeWindowMessageFilterEx");
80 if (pfnChangeWindowMessageFilterEx)
82 pfnChangeWindowMessageFilterEx(m_hWnd, TaskBarButtonCreated, MSGFLT_ALLOW, &cfs);
85 m_ProgList.m_pTaskbarList.Release();
86 if (FAILED(m_ProgList.m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList)))
87 m_ProgList.m_pTaskbarList = nullptr;
89 UpdateData(FALSE);
91 AddAnchor(IDC_SVNPROGRESS, TOP_LEFT, BOTTOM_RIGHT);
92 AddAnchor(IDC_TITLE_ANIMATE, TOP_LEFT, BOTTOM_RIGHT);
93 AddAnchor(IDC_PROGRESSLABEL, BOTTOM_LEFT, BOTTOM_CENTER);
94 AddAnchor(IDC_PROGRESSBAR, BOTTOM_CENTER, BOTTOM_RIGHT);
95 AddAnchor(IDC_INFOTEXT, BOTTOM_LEFT, BOTTOM_RIGHT);
96 AddAnchor(IDCANCEL, BOTTOM_RIGHT);
97 AddAnchor(IDOK, BOTTOM_RIGHT);
98 AddAnchor(IDC_LOGBUTTON, BOTTOM_RIGHT);
99 //SetPromptParentWindow(this->m_hWnd);
101 m_Animate.Open(IDR_DOWNLOAD);
102 m_ProgList.m_pAnimate = &m_Animate;
103 m_ProgList.m_pProgControl = &m_ProgCtrl;
104 m_ProgList.m_pProgressLabelCtrl = &m_ProgLableCtrl;
105 m_ProgList.m_pInfoCtrl = &m_InfoCtrl;
106 m_ProgList.m_pPostWnd = this;
107 m_ProgList.m_bSetTitle = true;
109 if (GetExplorerHWND())
110 CenterWindow(CWnd::FromHandle(GetExplorerHWND()));
111 EnableSaveRestore(L"GITProgressDlg");
113 m_background_brush.CreateSolidBrush(GetSysColor(COLOR_WINDOW));
114 m_ProgList.Init();
116 int autoClose = CRegDWORD(L"Software\\TortoiseGit\\AutoCloseGitProgress", 0);
117 CCmdLineParser parser(AfxGetApp()->m_lpCmdLine);
118 if (parser.HasKey(L"closeonend"))
119 autoClose = parser.GetLongVal(L"closeonend");
120 switch (autoClose)
122 case 1:
123 m_AutoClose = AUTOCLOSE_IF_NO_OPTIONS;
124 break;
125 case 2:
126 m_AutoClose = AUTOCLOSE_IF_NO_ERRORS;
127 break;
128 default:
129 m_AutoClose = AUTOCLOSE_NO;
130 break;
133 return TRUE;
136 LRESULT CGitProgressDlg::OnTaskbarBtnCreated(WPARAM wParam, LPARAM lParam)
138 m_ProgList.m_pTaskbarList.Release();
139 m_ProgList.m_pTaskbarList.CoCreateInstance(CLSID_TaskbarList);
140 return __super::OnTaskbarButtonCreated(wParam, lParam);
143 void CGitProgressDlg::OnBnClickedLogbutton()
145 ShowWindow(SW_HIDE);
146 m_PostCmdList.at(m_cMenuButton.GetCurrentEntry()).action();
147 EndDialog(IDOK);
151 void CGitProgressDlg::OnClose()
153 DialogEnableWindow(IDCANCEL, TRUE);
154 __super::OnClose();
157 void CGitProgressDlg::OnOK()
159 if ((m_ProgList.IsCancelled())&&(!m_ProgList.IsRunning()))
161 // I have made this wait a sensible amount of time (10 seconds) for the thread to finish
162 // You must be careful in the thread that after posting the WM_COMMAND/IDOK message, you
163 // don't do any more operations on the window which might require message passing
164 // If you try to send windows messages once we're waiting here, then the thread can't finished
165 // because the Window's message loop is blocked at this wait
166 WaitForSingleObject(m_ProgList.m_pThread->m_hThread, 10000);
167 __super::OnOK();
169 m_ProgList.Cancel();
172 void CGitProgressDlg::OnCancel()
174 if ((m_ProgList.IsCancelled())&&(!m_ProgList.IsRunning()))
175 __super::OnCancel();
176 m_ProgList.Cancel();
179 BOOL CGitProgressDlg::OnSetCursor(CWnd* pWnd, UINT nHitTest, UINT message)
181 if (!GetDlgItem(IDOK)->IsWindowEnabled())
183 // only show the wait cursor over the list control
184 if ((pWnd)&&(pWnd == GetDlgItem(IDC_SVNPROGRESS)))
186 HCURSOR hCur = LoadCursor(nullptr, IDC_WAIT);
187 SetCursor(hCur);
188 return TRUE;
191 if (pWnd && pWnd == GetDlgItem(IDC_INFOTEXT))
192 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
193 HCURSOR hCur = LoadCursor(nullptr, IDC_ARROW);
194 SetCursor(hCur);
195 return CResizableStandAloneDialog::OnSetCursor(pWnd, nHitTest, message);
198 BOOL CGitProgressDlg::PreTranslateMessage(MSG* pMsg)
200 if (m_hAccel && TranslateAccelerator(m_hWnd, m_hAccel, pMsg))
201 return TRUE;
202 if (pMsg->message == WM_KEYDOWN)
204 if (pMsg->wParam == VK_ESCAPE)
206 // pressing the ESC key should close the dialog. But since we disabled the escape
207 // key (so the user doesn't get the idea that he could simply undo an e.g. update)
208 // this won't work.
209 // So if the user presses the ESC key, change it to VK_RETURN so the dialog gets
210 // the impression that the OK button was pressed.
211 if ((!m_ProgList.IsRunning())&&(!GetDlgItem(IDCANCEL)->IsWindowEnabled())
212 &&(GetDlgItem(IDOK)->IsWindowEnabled())&&(GetDlgItem(IDOK)->IsWindowVisible()))
214 // since we convert ESC to RETURN, make sure the OK button has the focus.
215 GetDlgItem(IDOK)->SetFocus();
216 pMsg->wParam = VK_RETURN;
220 return __super::PreTranslateMessage(pMsg);
223 void CGitProgressDlg::OnEnSetfocusInfotext()
225 CString sTemp;
226 GetDlgItemText(IDC_INFOTEXT, sTemp);
227 if (sTemp.IsEmpty())
228 GetDlgItem(IDC_INFOTEXT)->HideCaret();
231 LRESULT CGitProgressDlg::OnCtlColorStatic(WPARAM wParam, LPARAM lParam)
233 HDC hDC = (HDC)wParam;
234 HWND hwndCtl = (HWND)lParam;
236 if (::GetDlgCtrlID(hwndCtl) == IDC_TITLE_ANIMATE)
238 CDC *pDC = CDC::FromHandle(hDC);
239 pDC->SetBkColor(GetSysColor(COLOR_WINDOW));
240 pDC->SetBkMode(TRANSPARENT);
241 return (LRESULT)(HBRUSH)m_background_brush.GetSafeHandle();
243 return FALSE;
246 HBRUSH CGitProgressDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
248 HBRUSH hbr;
249 if (pWnd->GetDlgCtrlID() == IDC_TITLE_ANIMATE)
251 pDC->SetBkColor(GetSysColor(COLOR_WINDOW)); // add this
252 pDC->SetBkMode(TRANSPARENT);
253 return (HBRUSH)m_background_brush.GetSafeHandle();
255 else
256 hbr = CResizableStandAloneDialog::OnCtlColor(pDC, pWnd, nCtlColor);
258 // TODO: Return a different brush if the default is not desired
259 return hbr;
262 LRESULT CGitProgressDlg::OnCmdEnd(WPARAM /*wParam*/, LPARAM /*lParam*/)
264 RefreshCursor();
266 DialogEnableWindow(IDCANCEL, FALSE);
267 DialogEnableWindow(IDOK, TRUE);
269 m_PostCmdList.clear();
270 if (m_ProgList.m_Command->m_PostCmdCallback)
271 m_ProgList.m_Command->m_PostCmdCallback(m_ProgList.DidErrorsOccur() ? 1 : 0, m_PostCmdList);
273 if (!m_PostCmdList.empty())
275 int i = 0;
276 for (const auto& entry : m_PostCmdList)
278 ++i;
279 m_cMenuButton.AddEntry(entry.label, entry.icon);
280 TCHAR accellerator = CStringUtils::GetAccellerator(entry.label);
281 if (accellerator == L'\0')
282 continue;
283 ++m_accellerators[accellerator].cnt;
284 if (m_accellerators[accellerator].cnt > 1)
285 m_accellerators[accellerator].id = -1;
286 else
287 m_accellerators[accellerator].id = i - 1;
290 if (m_accellerators.size())
292 LPACCEL lpaccelNew = (LPACCEL)LocalAlloc(LPTR, m_accellerators.size() * sizeof(ACCEL));
293 SCOPE_EXIT { LocalFree(lpaccelNew); };
294 i = 0;
295 for (auto& entry : m_accellerators)
297 lpaccelNew[i].cmd = (WORD)(WM_USER + 1 + entry.second.id);
298 lpaccelNew[i].fVirt = FVIRTKEY | FALT;
299 lpaccelNew[i].key = entry.first;
300 entry.second.wmid = lpaccelNew[i].cmd;
301 ++i;
303 m_hAccel = CreateAcceleratorTable(lpaccelNew, (int)m_accellerators.size());
305 m_cMenuButton.ShowWindow(SW_SHOW);
308 CWnd * pWndOk = GetDlgItem(IDOK);
309 if (pWndOk && ::IsWindow(pWndOk->GetSafeHwnd()))
311 SendMessage(DM_SETDEFID, IDOK);
312 GetDlgItem(IDOK)->SetFocus();
313 if (!m_ProgList.DidErrorsOccur() && (m_AutoClose == AUTOCLOSE_IF_NO_OPTIONS && m_PostCmdList.empty() || m_AutoClose == AUTOCLOSE_IF_NO_ERRORS))
314 PostMessage(WM_COMMAND, 1, (LPARAM)pWndOk->GetSafeHwnd());
317 return 0;
320 LRESULT CGitProgressDlg::OnCmdStart(WPARAM /*wParam*/, LPARAM /*lParam*/)
322 DialogEnableWindow(IDOK, FALSE);
323 DialogEnableWindow(IDCANCEL, TRUE);
325 return 0;
328 LRESULT CGitProgressDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam)
330 if (m_hAccel && message == WM_COMMAND && LOWORD(wParam) >= WM_USER && LOWORD(wParam) <= WM_USER + m_accellerators.size())
332 for (const auto& entry : m_accellerators)
334 if (entry.second.wmid != LOWORD(wParam))
335 continue;
336 if (entry.second.id == -1)
337 m_cMenuButton.PostMessage(WM_KEYDOWN, VK_F4, NULL);
338 else
340 m_cMenuButton.SetCurrentEntry(entry.second.id);
341 OnBnClickedLogbutton();
343 return 0;
347 return __super::DefWindowProc(message, wParam, lParam);