1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2008-2016 - 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.
21 #include "TortoiseProc.h"
22 #include "GitProgressDlg.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
)
36 CGitProgressDlg::~CGitProgressDlg()
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
)
57 ON_EN_SETFOCUS(IDC_INFOTEXT
, &CGitProgressDlg::OnEnSetfocusInfotext
)
59 ON_MESSAGE(WM_PROG_CMD_FINISH
, OnCmdEnd
)
60 ON_MESSAGE(WM_PROG_CMD_START
, OnCmdStart
)
61 ON_REGISTERED_MESSAGE(TaskBarButtonCreated
, OnTaskbarBtnCreated
)
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");
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;
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;
110 CenterWindow(CWnd::FromHandle(hWndExplorer
));
111 EnableSaveRestore(L
"GITProgressDlg");
113 m_background_brush
.CreateSolidBrush(GetSysColor(COLOR_WINDOW
));
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");
123 m_AutoClose
= AUTOCLOSE_IF_NO_OPTIONS
;
126 m_AutoClose
= AUTOCLOSE_IF_NO_ERRORS
;
129 m_AutoClose
= AUTOCLOSE_NO
;
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()
146 m_PostCmdList
.at(m_cMenuButton
.GetCurrentEntry()).action();
151 void CGitProgressDlg::OnClose()
153 DialogEnableWindow(IDCANCEL
, TRUE
);
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);
172 void CGitProgressDlg::OnCancel()
174 if ((m_ProgList
.IsCancelled())&&(!m_ProgList
.IsRunning()))
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
);
191 HCURSOR hCur
= LoadCursor(nullptr, IDC_ARROW
);
193 return CResizableStandAloneDialog::OnSetCursor(pWnd
, nHitTest
, message
);
196 BOOL
CGitProgressDlg::PreTranslateMessage(MSG
* pMsg
)
198 if (m_hAccel
&& TranslateAccelerator(m_hWnd
, m_hAccel
, pMsg
))
200 if (pMsg
->message
== WM_KEYDOWN
)
202 if (pMsg
->wParam
== VK_ESCAPE
)
204 // pressing the ESC key should close the dialog. But since we disabled the escape
205 // key (so the user doesn't get the idea that he could simply undo an e.g. update)
207 // So if the user presses the ESC key, change it to VK_RETURN so the dialog gets
208 // the impression that the OK button was pressed.
209 if ((!m_ProgList
.IsRunning())&&(!GetDlgItem(IDCANCEL
)->IsWindowEnabled())
210 &&(GetDlgItem(IDOK
)->IsWindowEnabled())&&(GetDlgItem(IDOK
)->IsWindowVisible()))
212 // since we convert ESC to RETURN, make sure the OK button has the focus.
213 GetDlgItem(IDOK
)->SetFocus();
214 pMsg
->wParam
= VK_RETURN
;
218 return __super::PreTranslateMessage(pMsg
);
221 void CGitProgressDlg::OnEnSetfocusInfotext()
224 GetDlgItemText(IDC_INFOTEXT
, sTemp
);
226 GetDlgItem(IDC_INFOTEXT
)->HideCaret();
229 LRESULT
CGitProgressDlg::OnCtlColorStatic(WPARAM wParam
, LPARAM lParam
)
231 HDC hDC
= (HDC
)wParam
;
232 HWND hwndCtl
= (HWND
)lParam
;
234 if (::GetDlgCtrlID(hwndCtl
) == IDC_TITLE_ANIMATE
)
236 CDC
*pDC
= CDC::FromHandle(hDC
);
237 pDC
->SetBkColor(GetSysColor(COLOR_WINDOW
));
238 pDC
->SetBkMode(TRANSPARENT
);
239 return (LRESULT
)(HBRUSH
)m_background_brush
.GetSafeHandle();
244 HBRUSH
CGitProgressDlg::OnCtlColor(CDC
* pDC
, CWnd
* pWnd
, UINT nCtlColor
)
247 if (pWnd
->GetDlgCtrlID() == IDC_TITLE_ANIMATE
)
249 pDC
->SetBkColor(GetSysColor(COLOR_WINDOW
)); // add this
250 pDC
->SetBkMode(TRANSPARENT
);
251 return (HBRUSH
)m_background_brush
.GetSafeHandle();
254 hbr
= CResizableStandAloneDialog::OnCtlColor(pDC
, pWnd
, nCtlColor
);
256 // TODO: Return a different brush if the default is not desired
260 LRESULT
CGitProgressDlg::OnCmdEnd(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
264 DialogEnableWindow(IDCANCEL
, FALSE
);
265 DialogEnableWindow(IDOK
, TRUE
);
267 m_PostCmdList
.clear();
268 if (m_ProgList
.m_Command
->m_PostCmdCallback
)
269 m_ProgList
.m_Command
->m_PostCmdCallback(m_ProgList
.DidErrorsOccur() ? 1 : 0, m_PostCmdList
);
271 if (!m_PostCmdList
.empty())
274 for (const auto& entry
: m_PostCmdList
)
277 m_cMenuButton
.AddEntry(entry
.icon
, entry
.label
);
278 TCHAR accellerator
= CStringUtils::GetAccellerator(entry
.label
);
279 if (accellerator
== L
'\0')
281 ++m_accellerators
[accellerator
].cnt
;
282 if (m_accellerators
[accellerator
].cnt
> 1)
283 m_accellerators
[accellerator
].id
= -1;
285 m_accellerators
[accellerator
].id
= i
- 1;
288 if (m_accellerators
.size())
290 LPACCEL lpaccelNew
= (LPACCEL
)LocalAlloc(LPTR
, m_accellerators
.size() * sizeof(ACCEL
));
291 SCOPE_EXIT
{ LocalFree(lpaccelNew
); };
293 for (auto& entry
: m_accellerators
)
295 lpaccelNew
[i
].cmd
= (WORD
)(WM_USER
+ 1 + entry
.second
.id
);
296 lpaccelNew
[i
].fVirt
= FVIRTKEY
| FALT
;
297 lpaccelNew
[i
].key
= entry
.first
;
298 entry
.second
.wmid
= lpaccelNew
[i
].cmd
;
301 m_hAccel
= CreateAcceleratorTable(lpaccelNew
, (int)m_accellerators
.size());
303 m_cMenuButton
.ShowWindow(SW_SHOW
);
306 CWnd
* pWndOk
= GetDlgItem(IDOK
);
307 if (pWndOk
&& ::IsWindow(pWndOk
->GetSafeHwnd()))
309 SendMessage(DM_SETDEFID
, IDOK
);
310 GetDlgItem(IDOK
)->SetFocus();
311 if (!m_ProgList
.DidErrorsOccur() && (m_AutoClose
== AUTOCLOSE_IF_NO_OPTIONS
&& m_PostCmdList
.empty() || m_AutoClose
== AUTOCLOSE_IF_NO_ERRORS
))
312 PostMessage(WM_COMMAND
, 1, (LPARAM
)pWndOk
->GetSafeHwnd());
318 LRESULT
CGitProgressDlg::OnCmdStart(WPARAM
/*wParam*/, LPARAM
/*lParam*/)
320 DialogEnableWindow(IDOK
, FALSE
);
321 DialogEnableWindow(IDCANCEL
, TRUE
);
326 LRESULT
CGitProgressDlg::DefWindowProc(UINT message
, WPARAM wParam
, LPARAM lParam
)
328 if (m_hAccel
&& message
== WM_COMMAND
&& LOWORD(wParam
) >= WM_USER
&& LOWORD(wParam
) <= WM_USER
+ m_accellerators
.size())
330 for (const auto& entry
: m_accellerators
)
332 if (entry
.second
.wmid
!= LOWORD(wParam
))
334 if (entry
.second
.id
== -1)
335 m_cMenuButton
.PostMessage(WM_KEYDOWN
, VK_F4
, NULL
);
338 m_cMenuButton
.SetCurrentEntry(entry
.second
.id
);
339 OnBnClickedLogbutton();
345 return __super::DefWindowProc(message
, wParam
, lParam
);