Fixed issue #2507: Support keyboard shortcuts in yes/no prompts
[TortoiseGit.git] / src / Utils / MiscUI / BrowseFolder.cpp
blob18bdf9483b99026333a83365597ad0d045160d0f
1 // TortoiseGit - a Windows shell extension for easy version control
3 // Copyright (C) 2003-2012 - TortoiseSVN
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 #include "stdafx.h"
20 #include <windowsx.h>
21 #include "BrowseFolder.h"
22 #include "SmartHandle.h"
23 #include <strsafe.h>
25 BOOL CBrowseFolder::m_bCheck = FALSE;
26 BOOL CBrowseFolder::m_bCheck2 = FALSE;
27 WNDPROC CBrowseFolder::CBProc = NULL;
28 HWND CBrowseFolder::checkbox = NULL;
29 HWND CBrowseFolder::checkbox2 = NULL;
30 HWND CBrowseFolder::ListView = NULL;
31 TCHAR CBrowseFolder::m_CheckText[200];
32 TCHAR CBrowseFolder::m_CheckText2[200];
33 CString CBrowseFolder::m_sDefaultPath;
34 bool CBrowseFolder::m_DisableCheckbox2WhenCheckbox1IsChecked = false;
37 CBrowseFolder::CBrowseFolder(void)
38 : m_style(0),
39 m_root(NULL)
41 memset(m_displayName, 0, sizeof(m_displayName));
42 memset(m_title, 0, sizeof(m_title));
43 memset(m_CheckText, 0, sizeof(m_CheckText));
46 CBrowseFolder::~CBrowseFolder(void)
50 //show the dialog
51 CBrowseFolder::retVal CBrowseFolder::Show(HWND parent, LPTSTR path, size_t pathlen, LPCTSTR szDefaultPath /* = NULL */)
53 CString temp;
54 temp = path;
55 CString sDefault;
56 if (szDefaultPath)
57 sDefault = szDefaultPath;
58 CBrowseFolder::retVal ret = Show(parent, temp, sDefault);
59 _tcscpy_s(path, pathlen, temp);
60 return ret;
62 CBrowseFolder::retVal CBrowseFolder::Show(HWND parent, CString& path, const CString& sDefaultPath /* = CString() */)
64 retVal ret = OK; //assume OK
65 m_sDefaultPath = sDefaultPath;
66 if (m_sDefaultPath.IsEmpty() && !path.IsEmpty())
68 // if the result path already contains a path, use that as the default path
69 m_sDefaultPath = path;
72 HRESULT hr;
74 // Create a new common open file dialog
75 IFileOpenDialog* pfd = NULL;
76 hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
77 if (SUCCEEDED(hr))
79 // Set the dialog as a folder picker
80 DWORD dwOptions;
81 if (SUCCEEDED(hr = pfd->GetOptions(&dwOptions)))
83 hr = pfd->SetOptions(dwOptions | FOS_PICKFOLDERS | FOS_FORCEFILESYSTEM | FOS_PATHMUSTEXIST);
86 // Set a title
87 if (SUCCEEDED(hr))
89 TCHAR * nl = _tcschr(m_title, '\n');
90 if (nl)
91 *nl = 0;
92 pfd->SetTitle(m_title);
95 // set the default folder
96 if (SUCCEEDED(hr))
98 typedef HRESULT (WINAPI *SHCIFPN)(PCWSTR pszPath, IBindCtx * pbc, REFIID riid, void ** ppv);
100 CAutoLibrary hLib = AtlLoadSystemLibraryUsingFullPath(L"shell32.dll");
101 if (hLib)
103 SHCIFPN pSHCIFPN = (SHCIFPN)GetProcAddress(hLib, "SHCreateItemFromParsingName");
104 if (pSHCIFPN)
106 IShellItem *psiDefault = 0;
107 hr = pSHCIFPN(m_sDefaultPath, NULL, IID_PPV_ARGS(&psiDefault));
108 if (SUCCEEDED(hr))
110 hr = pfd->SetFolder(psiDefault);
111 psiDefault->Release();
117 if (m_CheckText[0] != 0)
119 IFileDialogCustomize* pfdCustomize = 0;
120 hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdCustomize));
121 if (SUCCEEDED(hr))
123 pfdCustomize->StartVisualGroup(100, L"");
124 pfdCustomize->AddCheckButton(101, m_CheckText, FALSE);
125 if (m_CheckText2[0] != 0)
127 pfdCustomize->AddCheckButton(102, m_CheckText2, FALSE);
129 pfdCustomize->EndVisualGroup();
130 pfdCustomize->Release();
134 // Show the open file dialog
135 if (SUCCEEDED(hr) && SUCCEEDED(hr = pfd->Show(parent)))
137 // Get the selection from the user
138 IShellItem* psiResult = NULL;
139 hr = pfd->GetResult(&psiResult);
140 if (SUCCEEDED(hr))
142 PWSTR pszPath = NULL;
143 hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszPath);
144 if (SUCCEEDED(hr))
146 path = pszPath;
147 CoTaskMemFree(pszPath);
149 psiResult->Release();
151 IFileDialogCustomize* pfdCustomize = 0;
152 hr = pfd->QueryInterface(IID_PPV_ARGS(&pfdCustomize));
153 if (SUCCEEDED(hr))
155 pfdCustomize->GetCheckButtonState(101, &m_bCheck);
156 pfdCustomize->GetCheckButtonState(102, &m_bCheck2);
157 pfdCustomize->Release();
160 else
161 ret = CANCEL;
163 else
164 ret = CANCEL;
166 pfd->Release();
168 else
170 BROWSEINFO browseInfo = {};
171 browseInfo.hwndOwner = parent;
172 browseInfo.pidlRoot = m_root;
173 browseInfo.pszDisplayName = m_displayName;
174 browseInfo.lpszTitle = m_title;
175 browseInfo.ulFlags = m_style;
176 browseInfo.lParam = (LPARAM)this;
178 if ((_tcslen(m_CheckText) > 0)||(!m_sDefaultPath.IsEmpty()))
180 browseInfo.lpfn = BrowseCallBackProc;
183 PCIDLIST_ABSOLUTE itemIDList = SHBrowseForFolder(&browseInfo);
185 //is the dialog canceled?
186 if (!itemIDList)
187 ret = CANCEL;
189 if (ret != CANCEL)
191 if (!SHGetPathFromIDList(itemIDList, path.GetBuffer(MAX_PATH))) // MAX_PATH ok. Explorer can't handle paths longer than MAX_PATH.
192 ret = NOPATH;
194 path.ReleaseBuffer();
196 CoTaskMemFree((LPVOID)itemIDList);
200 return ret;
203 void CBrowseFolder::SetInfo(LPCTSTR title)
205 ASSERT(title);
207 if (title)
208 _tcscpy_s(m_title, 200, title);
211 void CBrowseFolder::SetCheckBoxText(LPCTSTR checktext)
213 ASSERT(checktext);
215 if (checktext)
216 _tcscpy_s(m_CheckText, 200, checktext);
219 void CBrowseFolder::SetCheckBoxText2(LPCTSTR checktext)
221 ASSERT(checktext);
223 if (checktext)
224 _tcscpy_s(m_CheckText2, 200, checktext);
227 void CBrowseFolder::SetFont(HWND hwnd,LPTSTR FontName,int FontSize)
230 HFONT hf;
231 LOGFONT lf={0};
232 HDC hdc=GetDC(hwnd);
234 GetObject(GetWindowFont(hwnd),sizeof(lf),&lf);
235 lf.lfWeight = FW_REGULAR;
236 lf.lfHeight = (LONG)FontSize;
237 StringCchCopy( lf.lfFaceName, _countof(lf.lfFaceName), FontName );
238 hf=CreateFontIndirect(&lf);
239 SetBkMode(hdc,OPAQUE);
240 SendMessage(hwnd,WM_SETFONT,(WPARAM)hf,TRUE);
241 ReleaseDC(hwnd,hdc);
245 int CBrowseFolder::BrowseCallBackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM /*lpData*/)
247 RECT ListViewRect,Dialog;
248 //Initialization callback message
249 if (uMsg == BFFM_INITIALIZED)
251 if (_tcslen(m_CheckText) > 0)
253 bool bSecondCheckbox = (_tcslen(m_CheckText2)!=0);
254 //Rectangles for getting the positions
255 checkbox = CreateWindowEx( 0,
256 _T("BUTTON"),
257 m_CheckText,
258 WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|BS_AUTOCHECKBOX,
259 0,100,100,50,
260 hwnd,
262 NULL,
263 NULL);
264 if (checkbox == NULL)
265 return 0;
267 if (bSecondCheckbox)
269 //Rectangles for getting the positions
270 checkbox2 = CreateWindowEx( 0,
271 _T("BUTTON"),
272 m_CheckText2,
273 WS_CHILD|WS_VISIBLE|WS_CLIPCHILDREN|BS_AUTOCHECKBOX,
274 0,100,100,50,
275 hwnd,
277 NULL,
278 NULL);
279 if (checkbox2 == NULL)
280 return 0;
283 ListView = FindWindowEx(hwnd,NULL,_T("SysTreeView32"),NULL);
284 if (ListView == NULL)
285 ListView = FindWindowEx(hwnd,NULL,_T("SHBrowseForFolder ShellNameSpace Control"),NULL);
287 if (ListView == NULL)
288 return 0;
290 //Gets the dimensions of the windows
291 GetWindowRect(hwnd,&Dialog);
292 GetWindowRect(ListView,&ListViewRect);
293 POINT pt;
294 pt.x = ListViewRect.left;
295 pt.y = ListViewRect.top;
296 ScreenToClient(hwnd, &pt);
297 ListViewRect.top = pt.y;
298 ListViewRect.left = pt.x;
299 pt.x = ListViewRect.right;
300 pt.y = ListViewRect.bottom;
301 ScreenToClient(hwnd, &pt);
302 ListViewRect.bottom = pt.y;
303 ListViewRect.right = pt.x;
304 //Sets the list view controls dimensions
305 SetWindowPos(ListView,0,ListViewRect.left,
306 bSecondCheckbox ? ListViewRect.top+40 : ListViewRect.top+20,
307 (ListViewRect.right-ListViewRect.left),
308 bSecondCheckbox ? (ListViewRect.bottom - ListViewRect.top)-40 : (ListViewRect.bottom - ListViewRect.top)-20,
309 SWP_NOZORDER);
310 //Sets the window positions of checkbox and dialog controls
311 SetWindowPos(checkbox,HWND_BOTTOM,ListViewRect.left,
312 ListViewRect.top,
313 (ListViewRect.right-ListViewRect.left),
315 SWP_NOZORDER);
316 if (bSecondCheckbox)
318 SetWindowPos(checkbox2,HWND_BOTTOM,ListViewRect.left,
319 ListViewRect.top+20,
320 (ListViewRect.right-ListViewRect.left),
322 SWP_NOZORDER);
324 HWND label = FindWindowEx(hwnd, NULL, _T("STATIC"), NULL);
325 if (label)
327 HFONT hFont = (HFONT)::SendMessage(label, WM_GETFONT, 0, 0);
328 LOGFONT lf = {0};
329 GetObject(hFont, sizeof(lf), &lf);
330 HFONT hf2 = CreateFontIndirect(&lf);
331 ::SendMessage(checkbox, WM_SETFONT, (WPARAM)hf2, TRUE);
332 if (bSecondCheckbox)
333 ::SendMessage(checkbox2, WM_SETFONT, (WPARAM)hf2, TRUE);
335 else
337 //Sets the fonts of static controls
338 SetFont(checkbox,_T("MS Sans Serif"),12);
339 if (bSecondCheckbox)
340 SetFont(checkbox2,_T("MS Sans Serif"),12);
343 // Subclass the checkbox control.
344 CBProc = (WNDPROC) SetWindowLongPtr(checkbox,GWLP_WNDPROC, (LONG_PTR) CheckBoxSubclassProc);
345 //Sets the checkbox to checked position
346 SendMessage(checkbox,BM_SETCHECK,(WPARAM)m_bCheck,0);
347 if (bSecondCheckbox)
349 CBProc = (WNDPROC) SetWindowLongPtr(checkbox2,GWLP_WNDPROC, (LONG_PTR) CheckBoxSubclassProc2);
350 SendMessage(checkbox2,BM_SETCHECK,(WPARAM)m_bCheck,0);
352 // send a resize message to the resized list view control. Otherwise it won't show
353 // up properly until the user resizes the window!
354 SendMessage(ListView, WM_SIZE, SIZE_RESTORED, MAKELONG(ListViewRect.right-ListViewRect.left, bSecondCheckbox ? (ListViewRect.bottom - ListViewRect.top)-40 : (ListViewRect.bottom - ListViewRect.top)-20));
357 // now set the default directory
358 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)(LPCTSTR)m_sDefaultPath);
360 if (uMsg == BFFM_SELCHANGED)
362 // Set the status window to the currently selected path.
363 TCHAR szDir[MAX_PATH] = {0};
364 if (SHGetPathFromIDList((LPITEMIDLIST)lParam, szDir))
366 SendMessage(hwnd,BFFM_SETSTATUSTEXT, 0, (LPARAM)szDir);
370 return 0;
373 LRESULT CBrowseFolder::CheckBoxSubclassProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
375 if (uMsg == WM_LBUTTONUP)
377 m_bCheck = (SendMessage(hwnd,BM_GETCHECK,0,0)==BST_UNCHECKED);
378 if (m_bCheck && m_DisableCheckbox2WhenCheckbox1IsChecked)
380 ::EnableWindow(checkbox2, !m_bCheck);
382 else
383 ::EnableWindow(checkbox2, true);
386 return CallWindowProc(CBProc, hwnd, uMsg,
387 wParam, lParam);
390 LRESULT CBrowseFolder::CheckBoxSubclassProc2(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
392 if (uMsg == WM_LBUTTONUP)
394 m_bCheck2 = (SendMessage(hwnd,BM_GETCHECK,0,0)==BST_UNCHECKED);
397 return CallWindowProc(CBProc, hwnd, uMsg,
398 wParam, lParam);