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