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