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 // ID line follows -- this is updated by SVN
27 // $Id: FileOrFolderSelect.cpp 6569 2009-03-15 14:33:03Z kimmov $
28
29 #include <windows.h>
30 #include <shlobj.h>
31 #include <sys/stat.h>
32 #include "UnicodeString.h"
33 #include "Environment.h"
34 #include "FileOrFolderSelect.h"
35 #include "paths.h"
36 #include "MergeApp.h"
37 #include "resource.h"
38
39 // VC 6 headers don't define these constants for folder browse dialog
40 // so define them here. Copied from shlobj.h
41 #ifndef BIF_EDITBOX
42 #define BIF_EDITBOX            0x0010   // Add an editbox to the dialog
43 #endif
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
47 #endif
48 #ifndef BIF_USENEWUI
49 #define BIF_USENEWUI           (BIF_NEWDIALOGSTYLE | BIF_EDITBOX)
50 #endif
51
52 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
53                 LPARAM lpData);
54 static void ConvertFilter(LPTSTR filterStr);
55
56 /** @brief Last selected folder for folder selection dialog. */
57 static String LastSelectedFolder;
58
59 /**
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
76  */
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*/)
80 {
81         path.clear(); // Clear output param
82
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};
86
87         // check if specified path is a file
88         if (initialPath && initialPath[0])
89         {
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)
94                 {
95                         String temp;
96                         paths_SplitFilename(initialPath, 0, &temp, 0);
97                         lstrcpy(sSelectedFile, temp.c_str());
98                 }
99         }
100
101         String filters = sfilter, title = stitle;
102         if (sfilter.empty())
103                 filters = _("All Files (*.*)|*.*||");
104         if (stitle.empty())
105                 title = _("Open");
106
107         // Convert extension mask from MFC style separators ('|')
108         //  to Win32 style separators ('\0')
109         LPTSTR filtersStr = &*filters.begin();
110         ConvertFilter(filtersStr);
111
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;
127
128         BOOL bRetVal = FALSE;
129         if (is_open)
130                 bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
131         else
132                 bRetVal = GetSaveFileName((OPENFILENAME *)&ofn);
133         // common file dialog populated sSelectedFile variable's buffer
134
135         if (bRetVal)
136                 path = sSelectedFile;
137         
138         return bRetVal;
139 }
140
141 /** 
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)
148  */
149 BOOL SelectFolder(String& path, LPCTSTR root_path /*=NULL*/, 
150                         const String& stitle /*=_T("")*/, 
151                         HWND hwndOwner /*=NULL*/) 
152 {
153         BROWSEINFO bi;
154         LPMALLOC pMalloc;
155         LPITEMIDLIST pidl;
156         TCHAR szPath[MAX_PATH] = {0};
157         BOOL bRet = FALSE;
158         String title = stitle;
159         if (root_path == NULL)
160                 LastSelectedFolder.clear();
161         else
162                 LastSelectedFolder = root_path;
163
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;
171
172         pidl = SHBrowseForFolder(&bi);
173
174         if (pidl)
175         {
176                 if (SHGetPathFromIDList(pidl, szPath))
177                 {
178                         path = szPath;
179                         bRet = TRUE;
180                 }
181
182                 SHGetMalloc(&pMalloc);
183                 pMalloc->Free(pidl);
184                 pMalloc->Release();
185         }
186         return bRet;
187 }
188
189 /**
190  * @brief Callback function for setting selected folder for folder dialog.
191  */
192 static int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam,
193                 LPARAM lpData)
194 {
195         // Look for BFFM_INITIALIZED
196         if (uMsg == BFFM_INITIALIZED)
197         {
198                 if (lpData)
199                         SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);
200                 else
201                         SendMessage(hwnd, BFFM_SETSELECTION, TRUE, (LPARAM)LastSelectedFolder.c_str());
202         }
203         else if (uMsg == BFFM_VALIDATEFAILED)
204         {
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);
208                 if (answer == IDYES)
209                 {
210                         if (!paths_CreateIfNeeded((TCHAR*)lParam))
211                         {
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);
213                         }
214                 }
215                 return 1;
216         }
217         return 0;
218 }
219
220 /** 
221  * @brief Shows file/folder selection dialog.
222  *
223  * We need this custom function so we can select files and folders with the
224  * same dialog.
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.
233  */
234 BOOL SelectFileOrFolder(HWND parent, String& path, LPCTSTR initialPath /*=NULL*/)
235 {
236         String title = _("Open");
237
238         // This will tell common file dialog what to show
239         // and also this will hold its return value
240         TCHAR sSelectedFile[MAX_PATH];
241
242         // check if specified path is a file
243         if (initialPath && initialPath[0])
244         {
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)
249                 {
250                         String temp;
251                         paths_SplitFilename(initialPath, 0, &temp, 0);
252                         lstrcpy(sSelectedFile, temp.c_str());
253                 }
254         }
255
256         String filters = _("All Files (*.*)|*.*||");
257
258         // Convert extension mask from MFC style separators ('|')
259         //  to Win32 style separators ('\0')
260         LPTSTR filtersStr = &*filters.begin();
261         ConvertFilter(filtersStr);
262
263         String dirSelTag = _("Folder Selection");
264
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?
268
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;
282
283         BOOL bRetVal = GetOpenFileName((OPENFILENAME *)&ofn);
284
285         if (bRetVal)
286         {
287                 path = sSelectedFile;
288                 struct _stati64 statBuffer;
289                 int nRetVal = _tstati64(path.c_str(), &statBuffer);
290                 if (nRetVal == -1)
291                 {
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);
296                 }
297         }
298         return bRetVal;
299 }
300
301
302 /** 
303  * @brief Helper function for converting filter format.
304  *
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.
308  *
309  * @param [in,out] filterStr
310  * - in Mask string to convert
311  * - out Converted string
312  */
313 static void ConvertFilter(LPTSTR filterStr)
314 {
315         while (TCHAR *ch = _tcschr(filterStr, '|'))
316         {
317                 filterStr = ch + 1;
318                 *ch = '\0';
319         }
320 }