]> git.saurik.com Git - wxWidgets.git/blob - src/msw/filedlg.cpp
two fixes from Justin Bradford
[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 && !wxUSE_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::SetPath(const wxString& path)
117 {
118 wxString ext;
119 wxSplitPath(path, &m_dir, &m_fileName, &ext);
120 if ( !ext.empty() )
121 m_fileName << _T('.') << ext;
122 }
123
124 int wxFileDialog::ShowModal()
125 {
126 HWND hWnd = 0;
127 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
128 if (!hWnd && wxTheApp->GetTopWindow())
129 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
130
131 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
132 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
133
134 *fileNameBuffer = wxT('\0');
135 *titleBuffer = wxT('\0');
136
137 long msw_flags = 0;
138 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
139 msw_flags |= OFN_HIDEREADONLY;
140 if ( m_dialogStyle & wxFILE_MUST_EXIST )
141 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
142
143 if (m_dialogStyle & wxMULTIPLE )
144 {
145 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
146 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
147 }
148
149 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
150 // standard dialog does by default
151 if ( !(m_dialogStyle & wxCHANGE_DIR) )
152 {
153 msw_flags |= OFN_NOCHANGEDIR;
154 }
155 /* chris elliott for some reason this does not work usefully if no extension
156 is given, as it test for junk instead of junk.ext
157 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
158 {
159 msw_flags |= OFN_OVERWRITEPROMPT;
160 }
161 */
162 OPENFILENAME of;
163 wxZeroMemory(of);
164
165 // the OPENFILENAME struct has been extended in newer version of
166 // comcdlg32.dll, but as we don't use the extended fields anyhow, set
167 // the struct size to the old value - otherwise, the programs compiled
168 // with new headers will not work with the old libraries
169 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
170 of.lStructSize = sizeof(OPENFILENAME) -
171 (sizeof(void *) + 2*sizeof(DWORD));
172 #else // old headers
173 of.lStructSize = sizeof(OPENFILENAME);
174 #endif
175
176 of.hwndOwner = hWnd;
177 of.lpstrTitle = WXSTRINGCAST m_message;
178 of.lpstrFileTitle = titleBuffer;
179 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
180
181 // Convert forward slashes to backslashes (file selector doesn't like
182 // forward slashes) and also squeeze multiple consecutive slashes into one
183 // as it doesn't like two backslashes in a row neither
184
185 wxString dir;
186 size_t i, len = m_dir.length();
187 dir.reserve(len);
188 for ( i = 0; i < len; i++ )
189 {
190 wxChar ch = m_dir[i];
191 switch ( ch )
192 {
193 case _T('/'):
194 // convert to backslash
195 ch = _T('\\');
196
197 // fall through
198
199 case _T('\\'):
200 while ( i < len - 1 )
201 {
202 wxChar chNext = m_dir[i + 1];
203 if ( chNext != _T('\\') && chNext != _T('/') )
204 break;
205
206 // ignore the next one, unless it is at the start of a UNC path
207 if (i > 0)
208 i++;
209 else
210 break;
211 }
212 // fall through
213
214 default:
215 // normal char
216 dir += ch;
217 }
218 }
219
220 of.lpstrInitialDir = dir.c_str();
221
222 of.Flags = msw_flags;
223
224
225 //=== Like Alejandro Sierra's wildcard modification >>===================
226 /*
227 In wxFileSelector you can put, instead of a single wild_card,
228 pairs of strings separated by '|'.
229 The first string is a description, and the
230 second is the wild card. You can put any number of pairs.
231
232 eg. "description1 (*.ex1)|*.ex1|description2 (*.ex2)|*.ex2"
233
234 If you put a single wild card, it works as before the modification.
235 */
236 //=======================================================================
237
238 wxString theFilter;
239 if ( wxStrlen(m_wildCard) == 0 )
240 theFilter = wxString(wxT("*.*"));
241 else
242 theFilter = m_wildCard ;
243 wxString filterBuffer;
244
245 if ( !wxStrchr( theFilter, wxT('|') ) ) { // only one filter ==> default text
246 filterBuffer.Printf(_("Files (%s)|%s"),
247 theFilter.c_str(), theFilter.c_str());
248 }
249 else { // more then one filter
250 filterBuffer = theFilter;
251
252 }
253
254 filterBuffer += wxT("|");
255 // Replace | with \0
256 for (i = 0; i < filterBuffer.Len(); i++ ) {
257 if ( filterBuffer.GetChar(i) == wxT('|') ) {
258 filterBuffer[i] = wxT('\0');
259 }
260 }
261
262 of.lpstrFilter = (LPTSTR)(const wxChar *)filterBuffer;
263 of.nFilterIndex = m_filterIndex + 1;
264
265 //=== Setting defaultFileName >>=========================================
266
267 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
268 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
269
270 of.lpstrFile = fileNameBuffer; // holds returned filename
271 of.nMaxFile = wxMAXPATH;
272
273 //== Execute FileDialog >>=================================================
274
275 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
276 : GetOpenFileName(&of)) != 0;
277
278 DWORD errCode = CommDlgExtendedError();
279
280 #ifdef __WIN32__
281 if (!success && (errCode == CDERR_STRUCTSIZE))
282 {
283 // The struct size has changed so try a smaller or bigger size
284
285 int oldStructSize = of.lStructSize;
286 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
287 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
288 : (GetOpenFileName(&of) != 0);
289 errCode = CommDlgExtendedError();
290
291 if (!success && (errCode == CDERR_STRUCTSIZE))
292 {
293 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
294 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
295 : (GetOpenFileName(&of) != 0);
296 }
297 }
298 #endif // __WIN32__
299
300 if ( success )
301 {
302 m_fileNames.Empty();
303
304 if ( ( m_dialogStyle & wxMULTIPLE ) &&
305 #if defined(OFN_EXPLORER)
306 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
307 #else
308 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
309 #endif // OFN_EXPLORER
310 )
311 {
312 #if defined(OFN_EXPLORER)
313 m_dir = fileNameBuffer;
314 i = of.nFileOffset;
315 m_fileName = &fileNameBuffer[i];
316 m_fileNames.Add(m_fileName);
317 i += m_fileName.Len() + 1;
318
319 while (fileNameBuffer[i] != wxT('\0'))
320 {
321 m_fileNames.Add(&fileNameBuffer[i]);
322 i += wxStrlen(&fileNameBuffer[i]) + 1;
323 }
324 #else
325 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
326 m_dir = toke.GetNextToken();
327 m_fileName = toke.GetNextToken();
328 m_fileNames.Add(m_fileName);
329
330 while (toke.HasMoreTokens())
331 m_fileNames.Add(toke.GetNextToken());
332 #endif // OFN_EXPLORER
333
334 wxString dir(m_dir);
335 if ( m_dir.Last() != _T('\\') )
336 dir += _T('\\');
337
338 m_path = dir + m_fileName;
339 }
340 else
341 {
342 //=== Adding the correct extension >>=================================
343
344 m_filterIndex = (int)of.nFilterIndex - 1;
345
346 if ( !of.nFileExtension ||
347 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
348 {
349 // User has typed a filename without an extension:
350 const wxChar* extension = filterBuffer;
351 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
352
353 for( int i = 0; i < maxFilter; i++ ) // get extension
354 extension = extension + wxStrlen( extension ) + 1;
355
356 m_fileName = AppendExtension(fileNameBuffer, extension);
357 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
358 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
359 }
360
361 m_path = fileNameBuffer;
362 m_fileName = wxFileNameFromPath(fileNameBuffer);
363 m_fileNames.Add(m_fileName);
364 m_dir = wxPathOnly(fileNameBuffer);
365 }
366 //=== Simulating the wxOVERWRITE_PROMPT >>============================
367 //should we also test for file save style ??
368 if ( (m_dialogStyle & wxOVERWRITE_PROMPT) &&
369 ::wxFileExists( fileNameBuffer ) )
370 {
371 wxString messageText;
372 messageText.Printf(_("File '%s' already exists.\nDo you want to replace it?"), fileNameBuffer);
373 if ( wxMessageBox(messageText, wxT("Save File As"), wxYES_NO | wxICON_EXCLAMATION ) != wxYES )
374 {
375 success = FALSE;
376 }
377 }
378 }
379 else
380 {
381 // common dialog failed - why?
382 #ifdef __WXDEBUG__
383 DWORD dwErr = CommDlgExtendedError();
384 if ( dwErr != 0 )
385 {
386 // this msg is only for developers
387 wxLogError(wxT("Common dialog failed with error code %0lx."),
388 dwErr);
389 }
390 //else: it was just cancelled
391 #endif
392 }
393
394 return success ? wxID_OK : wxID_CANCEL;
395
396 }
397
398 #endif // wxUSE_FILEDLG
399