OSDN Git Service

Replace paths_* with paths::*
[winmerge-jp/winmerge-jp.git] / Src / FileOrFolderSelect.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 //    WinMerge:  an interactive diff/merge utility
3 //    Copyright (C) 1997-2000  Thingamahoochie Software
4 //    Author: Dean Grimm
5 //
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.
10 //
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.
15 //
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.
19 //
20 /////////////////////////////////////////////////////////////////////////////
21 /** 
22  * @file  FileOrFolderSelect.cpp
23  *
24  * @brief Implementation of the file and folder selection routines.
25  */
26
27 #include <windows.h>
28 #include "FileOrFolderSelect.h"
29 #include <shlobj.h>
30 #include <sys/stat.h>
31 #include "Environment.h"
32 #include "paths.h"
33 #include "MergeApp.h"
34
35 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
36                 LPARAM lpData);
37 static void ConvertFilter(LPTSTR filterStr);
38
39 /** @brief Last selected folder for folder selection dialog. */
40 static String LastSelectedFolder;
41
42 /**
43  * @brief Helper function for selecting folder or file.
44  * This function shows standard Windows file selection dialog for selecting
45  * file or folder to open or file to save. The last parameter @p is_open selects
46  * between open or save modes. Biggest difference is that in save-mode Windows
47  * asks if user wants to override existing file.
48  * @param [in] parent Handle to parent window. Can be a NULL, but then
49  *     CMainFrame is used which can cause modality problems.
50  * @param [out] path Selected path is returned in this string
51  * @param [in] initialPath Initial path (and file) shown when dialog is opened
52  * @param [in] titleid Resource string ID for dialog title.
53  * @param [in] filterid 0 or STRING ID for filter string
54  *     - 0 means "All files (*.*)". Note the string formatting!
55  * @param [in] is_open Selects Open/Save -dialog (mode).
56  * @note Be careful when setting @p parent to NULL as there are potential
57  * modality problems with this. Dialog can be lost behind other windows!
58  * @param [in] defaultExtension Extension to append if user doesn't provide one
59  */
60 BOOL SelectFile(HWND parent, String& path, LPCTSTR initialPath /*=NULL*/,
61                 const String& stitle /*=_T("")*/, const String& sfilter /*=_T("")*/,
62                 BOOL is_open /*=TRUE*/, LPCTSTR defaultExtension /*=NULL*/)
63 {
64         path.clear(); // Clear output param
65
66         // This will tell common file dialog what to show
67         // and also this will hold its return value
68         TCHAR sSelectedFile[MAX_PATH] = {0};
69
70         // check if specified path is a file
71         if (initialPath && initialPath[0])
72         {
73                 // If initial path info includes a file
74                 // we put the bare filename into sSelectedFile
75                 // so the common file dialog will start up with that file selected
76                 if (paths::DoesPathExist(initialPath) == paths::IS_EXISTING_FILE)
77                 {
78                         String temp;
79                         paths::SplitFilename(initialPath, 0, &temp, 0);
80                         lstrcpy(sSelectedFile, temp.c_str());
81                 }
82         }
83
84         String filters = sfilter, title = stitle;
85         if (sfilter.empty())
86                 filters = _("All Files (*.*)|*.*||");
87         if (stitle.empty())
88                 title = _("Open");
89
90         // Convert extension mask from MFC style separators ('|')
91         //  to Win32 style separators ('\0')
92         LPTSTR filtersStr = &*filters.begin();
93         ConvertFilter(filtersStr);
94
95         OPENFILENAME_NT4 ofn = { sizeof OPENFILENAME_NT4 };
96         ofn.hwndOwner = parent;
97         ofn.lpstrFilter = filtersStr;
98         ofn.lpstrCustomFilter = NULL;
99         ofn.nFilterIndex = 1;
100         ofn.lpstrFile = sSelectedFile;
101         ofn.nMaxFile = MAX_PATH;
102         ofn.lpstrInitialDir = initialPath;
103         ofn.lpstrTitle = title.c_str();
104         ofn.lpstrFileTitle = NULL;
105         if (defaultExtension)
106                 ofn.lpstrDefExt = defaultExtension;
107         ofn.Flags = OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOCHANGEDIR;
108
109         BOOL bRetVal = FALSE;
110         if (is_open)
111                 bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
112         else
113                 bRetVal = GetSaveFileName((OPENFILENAME *)&ofn);
114         // common file dialog populated sSelectedFile variable's buffer
115
116         if (bRetVal)
117                 path = sSelectedFile;
118         
119         return bRetVal;
120 }
121
122 /** 
123  * @brief Helper function for selecting directory
124  * @param [out] path Selected path is returned in this string
125  * @param [in] root_path Initial path shown when dialog is opened
126  * @param [in] titleid Resource string ID for dialog title.
127  * @param [in] hwndOwner Handle to owner window or NULL
128  * @return TRUE if valid folder selected (not cancelled)
129  */
130 BOOL SelectFolder(String& path, LPCTSTR root_path /*=NULL*/, 
131                         const String& stitle /*=_T("")*/, 
132                         HWND hwndOwner /*=NULL*/) 
133 {
134         BROWSEINFO bi;
135         LPITEMIDLIST pidl;
136         TCHAR szPath[MAX_PATH] = {0};
137         BOOL bRet = FALSE;
138         String title = stitle;
139         if (root_path == NULL)
140                 LastSelectedFolder.clear();
141         else
142                 LastSelectedFolder = root_path;
143
144         bi.hwndOwner = hwndOwner;
145         bi.pidlRoot = NULL;  // Start from desktop folder
146         bi.pszDisplayName = szPath;
147         bi.lpszTitle = title.c_str();
148         bi.ulFlags = BIF_RETURNONLYFSDIRS | BIF_USENEWUI | BIF_VALIDATE;
149         bi.lpfn = BrowseCallbackProc;
150         bi.lParam = (LPARAM)root_path;
151
152         pidl = SHBrowseForFolder(&bi);
153         if (pidl)
154         {
155                 if (SHGetPathFromIDList(pidl, szPath))
156                 {
157                         path = szPath;
158                         bRet = TRUE;
159                 }
160                 CoTaskMemFree(pidl);
161         }
162         return bRet;
163 }
164
165 /**
166  * @brief Callback function for setting selected folder for folder dialog.
167  */
168 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
169                 LPARAM lpData)
170 {
171         // Look for BFFM_INITIALIZED
172         if (uMsg == BFFM_INITIALIZED)
173         {
174                 if (lpData)
175                         SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
176                 else
177                         SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)LastSelectedFolder.c_str());
178         }
179         else if (uMsg == BFFM_VALIDATEFAILED)
180         {
181                 String strMessage = (TCHAR *)lParam;
182                 strMessage += _T("\83t\83H\83\8b\83_\82Í\91\8dÝ\82µ\82Ü\82¹\82ñ\81B\8dì\90¬\82µ\82Ü\82·\82©?");
183                 int answer = MessageBox(hwnd, strMessage.c_str(), _T("\83t\83H\83\8b\83_\82Ì\8dì\90¬"), MB_YESNO);
184                 if (answer == IDYES)
185                 {
186                         if (!paths::CreateIfNeeded((TCHAR*)lParam))
187                         {
188                                 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);
189                         }
190                 }
191                 return 1;
192         }
193         return 0;
194 }
195
196 /** 
197  * @brief Shows file/folder selection dialog.
198  *
199  * We need this custom function so we can select files and folders with the
200  * same dialog.
201  * - If existing filename is selected return it
202  * - If filename in (CFileDialog) editbox and current folder doesn't form
203  * a valid path to file, return current folder.
204  * @param [in] parent Handle to parent window. Can be a NULL, but then
205  *     CMainFrame is used which can cause modality problems.
206  * @param [out] path Selected folder/filename
207  * @param [in] initialPath Initial file or folder shown/selected.
208  * @return TRUE if user choosed a file/folder, FALSE if user canceled dialog.
209  */
210 BOOL SelectFileOrFolder(HWND parent, String& path, LPCTSTR initialPath /*=NULL*/)
211 {
212         String title = _("Open");
213
214         // This will tell common file dialog what to show
215         // and also this will hold its return value
216         TCHAR sSelectedFile[MAX_PATH];
217
218         // check if specified path is a file
219         if (initialPath && initialPath[0])
220         {
221                 // If initial path info includes a file
222                 // we put the bare filename into sSelectedFile
223                 // so the common file dialog will start up with that file selected
224                 if (paths::DoesPathExist(initialPath) == paths::IS_EXISTING_FILE)
225                 {
226                         String temp;
227                         paths::SplitFilename(initialPath, 0, &temp, 0);
228                         lstrcpy(sSelectedFile, temp.c_str());
229                 }
230         }
231
232         String filters = _("All Files (*.*)|*.*||");
233
234         // Convert extension mask from MFC style separators ('|')
235         //  to Win32 style separators ('\0')
236         LPTSTR filtersStr = &*filters.begin();
237         ConvertFilter(filtersStr);
238
239         String dirSelTag = _("Folder Selection");
240
241         // Set initial filename to folder selection tag
242         dirSelTag += _T("."); // Treat it as filename
243         lstrcpy(sSelectedFile, dirSelTag.c_str()); // What is assignment above good for?
244
245         OPENFILENAME_NT4 ofn = { sizeof OPENFILENAME_NT4 };
246         ofn.hwndOwner = parent;
247         ofn.lpstrFilter = filtersStr;
248         ofn.lpstrCustomFilter = NULL;
249         ofn.nFilterIndex = 1;
250         ofn.lpstrFile = sSelectedFile;
251         ofn.nMaxFile = MAX_PATH;
252         ofn.lpstrInitialDir = initialPath;
253         ofn.lpstrTitle = title.c_str();
254         ofn.lpstrFileTitle = NULL;
255         ofn.Flags = OFN_HIDEREADONLY | OFN_PATHMUSTEXIST | OFN_NOTESTFILECREATE | OFN_NOCHANGEDIR;
256
257         BOOL bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
258
259         if (bRetVal)
260         {
261                 path = sSelectedFile;
262                 struct _stati64 statBuffer;
263                 int nRetVal = _tstati64(path.c_str(), &statBuffer);
264                 if (nRetVal == -1)
265                 {
266                         // We have a valid folder name, but propably garbage as a filename.
267                         // Return folder name
268                         String folder = paths::GetPathOnly(sSelectedFile);
269                         path = paths::AddTrailingSlash(folder);
270                 }
271         }
272         return bRetVal;
273 }
274
275
276 /** 
277  * @brief Helper function for converting filter format.
278  *
279  * MFC functions separate filter strings with | char which is also
280  * good choice to safe into resource. But WinAPI32 functions we use
281  * needs '\0' as separator. This function replaces '|'s with '\0's.
282  *
283  * @param [in,out] filterStr
284  * - in Mask string to convert
285  * - out Converted string
286  */
287 static void ConvertFilter(LPTSTR filterStr)
288 {
289         while (TCHAR *ch = _tcschr(filterStr, '|'))
290         {
291                 filterStr = ch + 1;
292                 *ch = '\0';
293         }
294 }