check better before overwriting files (patch 951868)
[wxWidgets.git] / src / msw / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "filedlg.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_FILEDLG && !defined(__SMARTPHONE__)
32
33 #ifndef WX_PRECOMP
34 #include "wx/utils.h"
35 #include "wx/msgdlg.h"
36 #include "wx/filedlg.h"
37 #include "wx/filefn.h"
38 #include "wx/intl.h"
39 #include "wx/log.h"
40 #include "wx/app.h"
41 #endif
42
43 #include "wx/msw/private.h"
44
45 #if !defined(__WIN32__) || defined(__WXWINCE__)
46 #include <commdlg.h>
47 #endif
48
49 #include <math.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "wx/filename.h"
54 #include "wx/tokenzr.h"
55
56 #ifndef OFN_EXPLORER
57 #define OFN_EXPLORER 0x00080000
58 #endif
59
60 // ----------------------------------------------------------------------------
61 // constants
62 // ----------------------------------------------------------------------------
63
64 #ifdef __WIN32__
65 # define wxMAXPATH 65534
66 #else
67 # define wxMAXPATH 1024
68 #endif
69
70 # define wxMAXFILE 1024
71
72 # define wxMAXEXT 5
73
74 // ============================================================================
75 // implementation
76 // ============================================================================
77
78 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
79
80 // ----------------------------------------------------------------------------
81 // wxFileDialog
82 // ----------------------------------------------------------------------------
83
84 wxFileDialog::wxFileDialog(wxWindow *parent,
85 const wxString& message,
86 const wxString& defaultDir,
87 const wxString& defaultFileName,
88 const wxString& wildCard,
89 long style,
90 const wxPoint& pos)
91 :wxFileDialogBase(parent, message, defaultDir, defaultFileName, wildCard, style, pos)
92
93 {
94 if ( ( m_dialogStyle & wxMULTIPLE ) && ( m_dialogStyle & wxSAVE ) )
95 m_dialogStyle &= ~wxMULTIPLE;
96 }
97
98 void wxFileDialog::GetPaths(wxArrayString& paths) const
99 {
100 paths.Empty();
101
102 wxString dir(m_dir);
103 if ( m_dir.Last() != _T('\\') )
104 dir += _T('\\');
105
106 size_t count = m_fileNames.GetCount();
107 for ( size_t n = 0; n < count; n++ )
108 {
109 if (wxFileName(m_fileNames[n]).IsAbsolute())
110 paths.Add(m_fileNames[n]);
111 else
112 paths.Add(dir + m_fileNames[n]);
113 }
114 }
115
116 void wxFileDialog::GetFilenames(wxArrayString& files) const
117 {
118 files = m_fileNames;
119 }
120
121 void wxFileDialog::SetPath(const wxString& path)
122 {
123 wxString ext;
124 wxSplitPath(path, &m_dir, &m_fileName, &ext);
125 if ( !ext.empty() )
126 m_fileName << _T('.') << ext;
127 }
128
129 int wxFileDialog::ShowModal()
130 {
131 HWND hWnd = 0;
132 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
133 if (!hWnd && wxTheApp->GetTopWindow())
134 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
135
136 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
137 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
138
139 *fileNameBuffer = wxT('\0');
140 *titleBuffer = wxT('\0');
141
142 #if WXWIN_COMPATIBILITY_2_4
143 long msw_flags = 0;
144 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
145 msw_flags |= OFN_HIDEREADONLY;
146 #else
147 long msw_flags = OFN_HIDEREADONLY;
148 #endif
149
150 if ( m_dialogStyle & wxFILE_MUST_EXIST )
151 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
152
153 if (m_dialogStyle & wxMULTIPLE )
154 {
155 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
156 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
157 }
158
159 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
160 // standard dialog does by default
161 if ( !(m_dialogStyle & wxCHANGE_DIR) )
162 {
163 msw_flags |= OFN_NOCHANGEDIR;
164 }
165
166 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
167 {
168 msw_flags |= OFN_OVERWRITEPROMPT;
169 }
170
171 OPENFILENAME of;
172 wxZeroMemory(of);
173
174 // the OPENFILENAME struct has been extended in newer version of
175 // comcdlg32.dll, but as we don't use the extended fields anyhow, set
176 // the struct size to the old value - otherwise, the programs compiled
177 // with new headers will not work with the old libraries
178 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
179 of.lStructSize = sizeof(OPENFILENAME) -
180 (sizeof(void *) + 2*sizeof(DWORD));
181 #else // old headers
182 of.lStructSize = sizeof(OPENFILENAME);
183 #endif
184
185 of.hwndOwner = hWnd;
186 of.lpstrTitle = WXSTRINGCAST m_message;
187 of.lpstrFileTitle = titleBuffer;
188 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
189
190 // Convert forward slashes to backslashes (file selector doesn't like
191 // forward slashes) and also squeeze multiple consecutive slashes into one
192 // as it doesn't like two backslashes in a row neither
193
194 wxString dir;
195 size_t i, len = m_dir.length();
196 dir.reserve(len);
197 for ( i = 0; i < len; i++ )
198 {
199 wxChar ch = m_dir[i];
200 switch ( ch )
201 {
202 case _T('/'):
203 // convert to backslash
204 ch = _T('\\');
205
206 // fall through
207
208 case _T('\\'):
209 while ( i < len - 1 )
210 {
211 wxChar chNext = m_dir[i + 1];
212 if ( chNext != _T('\\') && chNext != _T('/') )
213 break;
214
215 // ignore the next one, unless it is at the start of a UNC path
216 if (i > 0)
217 i++;
218 else
219 break;
220 }
221 // fall through
222
223 default:
224 // normal char
225 dir += ch;
226 }
227 }
228
229 of.lpstrInitialDir = dir.c_str();
230
231 of.Flags = msw_flags;
232
233 wxArrayString wildDescriptions, wildFilters;
234
235 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
236
237 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
238
239 wxString filterBuffer;
240
241 for (i = 0; i < items ; i++)
242 {
243 filterBuffer += wildDescriptions[i];
244 filterBuffer += wxT("|");
245 filterBuffer += wildFilters[i];
246 filterBuffer += wxT("|");
247 }
248
249 // Replace | with \0
250 for (i = 0; i < filterBuffer.Len(); i++ ) {
251 if ( filterBuffer.GetChar(i) == wxT('|') ) {
252 filterBuffer[i] = wxT('\0');
253 }
254 }
255
256 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
257 of.nFilterIndex = m_filterIndex + 1;
258
259 //=== Setting defaultFileName >>=========================================
260
261 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
262 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
263
264 of.lpstrFile = fileNameBuffer; // holds returned filename
265 of.nMaxFile = wxMAXPATH;
266
267 // we must set the default extension because otherwise Windows would check
268 // for the existing of a wrong file with wxOVERWRITE_PROMPT (i.e. if the
269 // user types "foo" and the default extension is ".bar" we should force it
270 // to check for "foo.bar" existence and not "foo")
271 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
272 if (m_dialogStyle & wxSAVE)
273 {
274 const wxChar* extension = filterBuffer;
275 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
276
277 for( int i = 0; i < maxFilter; i++ ) // get extension
278 extension = extension + wxStrlen( extension ) + 1;
279
280 // use dummy name a to avoid assert in AppendExtension
281 defextBuffer = AppendExtension(wxT("a"), extension);
282 if (defextBuffer.StartsWith(wxT("a.")))
283 {
284 defextBuffer.Mid(2);
285 of.lpstrDefExt = defextBuffer.c_str();
286 }
287 }
288
289 //== Execute FileDialog >>=================================================
290
291 //== Execute FileDialog >>=================================================
292
293 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
294 : GetOpenFileName(&of)) != 0;
295
296 DWORD errCode = CommDlgExtendedError();
297
298 #ifdef __WIN32__
299 if (!success && (errCode == CDERR_STRUCTSIZE))
300 {
301 // The struct size has changed so try a smaller or bigger size
302
303 int oldStructSize = of.lStructSize;
304 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
305 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
306 : (GetOpenFileName(&of) != 0);
307 errCode = CommDlgExtendedError();
308
309 if (!success && (errCode == CDERR_STRUCTSIZE))
310 {
311 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
312 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
313 : (GetOpenFileName(&of) != 0);
314 }
315 }
316 #endif // __WIN32__
317
318 if ( success )
319 {
320 m_fileNames.Empty();
321
322 if ( ( m_dialogStyle & wxMULTIPLE ) &&
323 #if defined(OFN_EXPLORER)
324 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
325 #else
326 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
327 #endif // OFN_EXPLORER
328 )
329 {
330 #if defined(OFN_EXPLORER)
331 m_dir = fileNameBuffer;
332 i = of.nFileOffset;
333 m_fileName = &fileNameBuffer[i];
334 m_fileNames.Add(m_fileName);
335 i += m_fileName.Len() + 1;
336
337 while (fileNameBuffer[i] != wxT('\0'))
338 {
339 m_fileNames.Add(&fileNameBuffer[i]);
340 i += wxStrlen(&fileNameBuffer[i]) + 1;
341 }
342 #else
343 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
344 m_dir = toke.GetNextToken();
345 m_fileName = toke.GetNextToken();
346 m_fileNames.Add(m_fileName);
347
348 while (toke.HasMoreTokens())
349 m_fileNames.Add(toke.GetNextToken());
350 #endif // OFN_EXPLORER
351
352 wxString dir(m_dir);
353 if ( m_dir.Last() != _T('\\') )
354 dir += _T('\\');
355
356 m_path = dir + m_fileName;
357 m_filterIndex = (int)of.nFilterIndex - 1;
358 }
359 else
360 {
361 //=== Adding the correct extension >>=================================
362
363 m_filterIndex = (int)of.nFilterIndex - 1;
364
365 if ( !of.nFileExtension ||
366 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
367 {
368 // User has typed a filename without an extension:
369 const wxChar* extension = filterBuffer;
370 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
371
372 for( int i = 0; i < maxFilter; i++ ) // get extension
373 extension = extension + wxStrlen( extension ) + 1;
374
375 m_fileName = AppendExtension(fileNameBuffer, extension);
376 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
377 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
378 }
379
380 m_path = fileNameBuffer;
381 m_fileName = wxFileNameFromPath(fileNameBuffer);
382 m_fileNames.Add(m_fileName);
383 m_dir = wxPathOnly(fileNameBuffer);
384 }
385 }
386 else
387 {
388 // common dialog failed - why?
389 #ifdef __WXDEBUG__
390 DWORD dwErr = CommDlgExtendedError();
391 if ( dwErr != 0 )
392 {
393 // this msg is only for developers
394 wxLogError(wxT("Common dialog failed with error code %0lx."),
395 dwErr);
396 }
397 //else: it was just cancelled
398 #endif
399 }
400
401 return success ? wxID_OK : wxID_CANCEL;
402
403 }
404
405 #endif // wxUSE_FILEDLG
406