OSDN Git Service

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