1 /////////////////////////////////////////////////////////////////////////////
2 // WinMerge: an interactive diff/merge utility
3 // Copyright (C) 1997-2000 Thingamahoochie Software
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 2 of the License, or
9 // (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
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 /////////////////////////////////////////////////////////////////////////////
22 * @file FileOrFolderSelect.cpp
24 * @brief Implementation of the file and folder selection routines.
26 // ID line follows -- this is updated by SVN
27 // $Id: FileOrFolderSelect.cpp 6569 2009-03-15 14:33:03Z kimmov $
32 #include "UnicodeString.h"
33 #include "Environment.h"
34 #include "FileOrFolderSelect.h"
39 // VC 6 headers don't define these constants for folder browse dialog
40 // so define them here. Copied from shlobj.h
42 #define BIF_EDITBOX 0x0010 // Add an editbox to the dialog
44 #ifndef BIF_NEWDIALOGSTYLE
45 #define BIF_NEWDIALOGSTYLE 0x0040 // Use the new dialog layout with the ability to resize
46 // Caller needs to call OleInitialize() before using this API
49 #define BIF_USENEWUI (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
52 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
54 static void ConvertFilter(LPTSTR filterStr);
56 /** @brief Last selected folder for folder selection dialog. */
57 static String LastSelectedFolder;
60 * @brief Helper function for selecting folder or file.
61 * This function shows standard Windows file selection dialog for selecting
62 * file or folder to open or file to save. The last parameter @p is_open selects
63 * between open or save modes. Biggest difference is that in save-mode Windows
64 * asks if user wants to override existing file.
65 * @param [in] parent Handle to parent window. Can be a NULL, but then
66 * CMainFrame is used which can cause modality problems.
67 * @param [out] path Selected path is returned in this string
68 * @param [in] initialPath Initial path (and file) shown when dialog is opened
69 * @param [in] titleid Resource string ID for dialog title.
70 * @param [in] filterid 0 or STRING ID for filter string
71 * - 0 means "All files (*.*)". Note the string formatting!
72 * @param [in] is_open Selects Open/Save -dialog (mode).
73 * @note Be careful when setting @p parent to NULL as there are potential
74 * modality problems with this. Dialog can be lost behind other windows!
75 * @param [in] defaultExtension Extension to append if user doesn't provide one
77 BOOL SelectFile(HWND parent, String& path, LPCTSTR initialPath /*=NULL*/,
78 const String& stitle /*=_T("")*/, const String& sfilter /*=_T("")*/,
79 BOOL is_open /*=TRUE*/, LPCTSTR defaultExtension /*=NULL*/)
81 path.clear(); // Clear output param
83 // This will tell common file dialog what to show
84 // and also this will hold its return value
85 TCHAR sSelectedFile[MAX_PATH] = {0};
87 // check if specified path is a file
88 if (initialPath && initialPath[0])
90 // If initial path info includes a file
91 // we put the bare filename into sSelectedFile
92 // so the common file dialog will start up with that file selected
93 if (paths_DoesPathExist(initialPath) == IS_EXISTING_FILE)
96 paths_SplitFilename(initialPath, 0, &temp, 0);
97 lstrcpy(sSelectedFile, temp.c_str());
101 String filters = sfilter, title = stitle;
103 filters = _("All Files (*.*)|*.*||");
107 // Convert extension mask from MFC style separators ('|')
108 // to Win32 style separators ('\0')
109 LPTSTR filtersStr = &*filters.begin();
110 ConvertFilter(filtersStr);
112 OPENFILENAME_NT4 ofn;
113 memset(&ofn, 0, sizeof(ofn));
114 ofn.lStructSize = sizeof(ofn);
115 ofn.hwndOwner = parent;
116 ofn.lpstrFilter = filtersStr;
117 ofn.lpstrCustomFilter = NULL;
118 ofn.nFilterIndex = 1;
119 ofn.lpstrFile = sSelectedFile;
120 ofn.nMaxFile = MAX_PATH;
121 ofn.lpstrInitialDir = initialPath;
122 ofn.lpstrTitle = title.c_str();
123 ofn.lpstrFileTitle = NULL;
124 if (defaultExtension)
125 ofn.lpstrDefExt = defaultExtension;
126 ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
128 BOOL bRetVal = FALSE;
130 bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
132 bRetVal = GetSaveFileName((OPENFILENAME *)&ofn);
133 // common file dialog populated sSelectedFile variable's buffer
136 path = sSelectedFile;
142 * @brief Helper function for selecting directory
143 * @param [out] path Selected path is returned in this string
144 * @param [in] root_path Initial path shown when dialog is opened
145 * @param [in] titleid Resource string ID for dialog title.
146 * @param [in] hwndOwner Handle to owner window or NULL
147 * @return TRUE if valid folder selected (not cancelled)
149 BOOL SelectFolder(String& path, LPCTSTR root_path /*=NULL*/,
150 const String& stitle /*=_T("")*/,
151 HWND hwndOwner /*=NULL*/)
156 TCHAR szPath[MAX_PATH] = {0};
158 String title = stitle;
159 if (root_path == NULL)
160 LastSelectedFolder.clear();
162 LastSelectedFolder = root_path;
164 bi.hwndOwner = hwndOwner;
165 bi.pidlRoot = NULL; // Start from desktop folder
166 bi.pszDisplayName = szPath;
167 bi.lpszTitle = title.c_str();
168 bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_VALIDATE;
169 bi.lpfn = BrowseCallbackProc;
170 bi.lParam = (LPARAM)root_path;
172 pidl = SHBrowseForFolder(&bi);
176 if (SHGetPathFromIDList(pidl, szPath))
182 SHGetMalloc(&pMalloc);
190 * @brief Callback function for setting selected folder for folder dialog.
192 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
195 // Look for BFFM_INITIALIZED
196 if (uMsg == BFFM_INITIALIZED)
199 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
201 SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)LastSelectedFolder.c_str());
203 else if (uMsg == BFFM_VALIDATEFAILED)
205 String strMessage = (TCHAR *)lParam;
206 strMessage += _T("
\83t
\83H
\83\8b\83_
\82Í
\91¶
\8dÝ
\82µ
\82Ü
\82¹
\82ñ
\81B
\8dì
\90¬
\82µ
\82Ü
\82·
\82©?");
207 int answer = MessageBox(hwnd, strMessage.c_str(), _T("
\83t
\83H
\83\8b\83_
\82Ì
\8dì
\90¬"), MB_YESNO);
210 if (!paths_CreateIfNeeded((TCHAR*)lParam))
212 MessageBox(hwnd, _T("
\83t
\83H
\83\8b\83_
\82Ì
\8dì
\90¬
\82É
\8e¸
\94s
\82µ
\82Ü
\82µ
\82½"), _T("
\83t
\83H
\83\8b\83_
\82Ì
\8dì
\90¬"), MB_OK | MB_ICONWARNING);
221 * @brief Shows file/folder selection dialog.
223 * We need this custom function so we can select files and folders with the
225 * - If existing filename is selected return it
226 * - If filename in (CFileDialog) editbox and current folder doesn't form
227 * a valid path to file, return current folder.
228 * @param [in] parent Handle to parent window. Can be a NULL, but then
229 * CMainFrame is used which can cause modality problems.
230 * @param [out] path Selected folder/filename
231 * @param [in] initialPath Initial file or folder shown/selected.
232 * @return TRUE if user choosed a file/folder, FALSE if user canceled dialog.
234 BOOL SelectFileOrFolder(HWND parent, String& path, LPCTSTR initialPath /*=NULL*/)
236 String title = _("Open");
238 // This will tell common file dialog what to show
239 // and also this will hold its return value
240 TCHAR sSelectedFile[MAX_PATH];
242 // check if specified path is a file
243 if (initialPath && initialPath[0])
245 // If initial path info includes a file
246 // we put the bare filename into sSelectedFile
247 // so the common file dialog will start up with that file selected
248 if (paths_DoesPathExist(initialPath) == IS_EXISTING_FILE)
251 paths_SplitFilename(initialPath, 0, &temp, 0);
252 lstrcpy(sSelectedFile, temp.c_str());
256 String filters = _("All Files (*.*)|*.*||");
258 // Convert extension mask from MFC style separators ('|')
259 // to Win32 style separators ('\0')
260 LPTSTR filtersStr = &*filters.begin();
261 ConvertFilter(filtersStr);
263 String dirSelTag = _("Folder Selection");
265 // Set initial filename to folder selection tag
266 dirSelTag += _T("."); // Treat it as filename
267 lstrcpy(sSelectedFile, dirSelTag.c_str()); // What is assignment above good for?
269 OPENFILENAME_NT4 ofn;
270 memset(&ofn, 0, sizeof(ofn));
271 ofn.lStructSize = sizeof(ofn);
272 ofn.hwndOwner = parent;
273 ofn.lpstrFilter = filtersStr;
274 ofn.lpstrCustomFilter = NULL;
275 ofn.nFilterIndex = 1;
276 ofn.lpstrFile = sSelectedFile;
277 ofn.nMaxFile = MAX_PATH;
278 ofn.lpstrInitialDir = initialPath;
279 ofn.lpstrTitle = title.c_str();
280 ofn.lpstrFileTitle = NULL;
281 ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOTESTFILECREATE | OFN_NOCHANGEDIR;
283 BOOL bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
287 path = sSelectedFile;
288 struct _stati64 statBuffer;
289 int nRetVal = _tstati64(path.c_str(), &statBuffer);
292 // We have a valid folder name, but propably garbage as a filename.
293 // Return folder name
294 String folder = paths_GetPathOnly(sSelectedFile);
295 path = paths_AddTrailingSlash(folder);
303 * @brief Helper function for converting filter format.
305 * MFC functions separate filter strings with | char which is also
306 * good choice to safe into resource. But WinAPI32 functions we use
307 * needs '\0' as separator. This function replaces '|'s with '\0's.
309 * @param [in,out] filterStr
310 * - in Mask string to convert
311 * - out Converted string
313 static void ConvertFilter(LPTSTR filterStr)
315 while (TCHAR *ch = _tcschr(filterStr, '|'))