]> git.saurik.com Git - wxWidgets.git/blame - src/msw/filedlg.cpp
Under Cygwin, in platform.h default to wxMSW unless otherwise specified.
[wxWidgets.git] / src / msw / filedlg.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
f6bcfd97 2// Name: src/msw/filedlg.cpp
2bda0e17
KB
3// Purpose: wxFileDialog
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
8// Copyright: (c) Julian Smart and Markus Holzem
e15e548b 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
f6bcfd97
BP
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17 20#ifdef __GNUG__
ba681060 21 #pragma implementation "filedlg.h"
2bda0e17
KB
22#endif
23
24// For compilers that support precompilation, includes "wx.h".
25#include "wx/wxprec.h"
26
27#ifdef __BORLANDC__
ba681060 28 #pragma hdrstop
2bda0e17
KB
29#endif
30
1e6feb95
VZ
31#if wxUSE_FILEDLG
32
2bda0e17 33#ifndef WX_PRECOMP
ba681060
VZ
34 #include "wx/utils.h"
35 #include "wx/msgdlg.h"
36 #include "wx/dialog.h"
37 #include "wx/filedlg.h"
2b5f62a0 38 #include "wx/filefn.h"
ba681060 39 #include "wx/intl.h"
2662e49e 40 #include "wx/log.h"
f6bcfd97 41 #include "wx/app.h"
8f177c8e 42#endif
2bda0e17 43
f6bcfd97
BP
44#include "wx/msw/private.h"
45
b4da152e 46#if !defined(__WIN32__) || defined(__SALFORDC__)
ba681060 47 #include <commdlg.h>
2bda0e17
KB
48#endif
49
2bda0e17
KB
50#include <math.h>
51#include <stdlib.h>
52#include <string.h>
53
8f177c8e
VZ
54#include "wx/tokenzr.h"
55
6e8aa701
VZ
56#ifndef OFN_EXPLORER
57 #define OFN_EXPLORER 0x00080000
58#endif
59
f6bcfd97
BP
60// ----------------------------------------------------------------------------
61// constants
62// ----------------------------------------------------------------------------
63
64#ifdef __WIN32__
2b5f62a0 65# define wxMAXPATH 65534
f6bcfd97
BP
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// ----------------------------------------------------------------------------
79// wxWin macros
80// ----------------------------------------------------------------------------
81
8f177c8e 82IMPLEMENT_CLASS(wxFileDialog, wxDialog)
2bda0e17 83
f6bcfd97
BP
84// ----------------------------------------------------------------------------
85// global functions
86// ----------------------------------------------------------------------------
87
837e5743
OK
88wxString wxFileSelector(const wxChar *title,
89 const wxChar *defaultDir,
90 const wxChar *defaultFileName,
91 const wxChar *defaultExtension,
92 const wxChar *filter,
ba681060
VZ
93 int flags,
94 wxWindow *parent,
95 int x, int y)
2bda0e17 96{
1f2f0331
VZ
97 // In the original implementation, defaultExtension is passed to the
98 // lpstrDefExt member of OPENFILENAME. This extension, if non-NULL, is
99 // appended to the filename if the user fails to type an extension. The new
100 // implementation (taken from wxFileSelectorEx) appends the extension
101 // automatically, by looking at the filter specification. In fact this
102 // should be better than the native Microsoft implementation because
103 // Windows only allows *one* default extension, whereas here we do the
104 // right thing depending on the filter the user has chosen.
105
106 // If there's a default extension specified but no filter, we create a
107 // suitable filter.
108
109 wxString filter2;
e15e548b 110 if ( defaultExtension && !filter )
223d09f6 111 filter2 = wxString(wxT("*.")) + defaultExtension;
e15e548b
VZ
112 else if ( filter )
113 filter2 = filter;
114
115 wxString defaultDirString;
116 if (defaultDir)
117 defaultDirString = defaultDir;
e15e548b
VZ
118
119 wxString defaultFilenameString;
120 if (defaultFileName)
121 defaultFilenameString = defaultFileName;
1f2f0331
VZ
122
123 wxFileDialog fileDialog(parent, title, defaultDirString,
124 defaultFilenameString, filter2,
125 flags, wxPoint(x, y));
837e5743 126 if( wxStrlen(defaultExtension) != 0 )
1f2f0331 127 {
954f4710 128 int filterFind = 0,
1f2f0331
VZ
129 filterIndex = 0;
130
131 for( unsigned int i = 0; i < filter2.Len(); i++ )
132 {
223d09f6 133 if( filter2.GetChar(i) == wxT('|') )
1f2f0331
VZ
134 {
135 // save the start index of the new filter
136 unsigned int is = i++;
1f2f0331
VZ
137
138 // find the end of the filter
139 for( ; i < filter2.Len(); i++ )
140 {
223d09f6 141 if(filter2[i] == wxT('|'))
1f2f0331
VZ
142 break;
143 }
144
145 if( i-is-1 > 0 && is+1 < filter2.Len() )
146 {
574c0bbf 147 if( filter2.Mid(is+1,i-is-1).Contains(defaultExtension) )
1f2f0331
VZ
148 {
149 filterFind = filterIndex;
150 break;
151 }
152 }
954f4710
VZ
153
154 filterIndex++;
1f2f0331
VZ
155 }
156 }
157
158 fileDialog.SetFilterIndex(filterFind);
159 }
160
3ca6a5f0 161 wxString filename;
e15e548b 162 if ( fileDialog.ShowModal() == wxID_OK )
1f2f0331 163 {
3ca6a5f0 164 filename = fileDialog.GetPath();
1f2f0331 165 }
3ca6a5f0
BP
166
167 return filename;
2bda0e17
KB
168}
169
2bda0e17 170
837e5743
OK
171wxString wxFileSelectorEx(const wxChar *title,
172 const wxChar *defaultDir,
173 const wxChar *defaultFileName,
e15e548b 174 int* defaultFilterIndex,
837e5743 175 const wxChar *filter,
e15e548b
VZ
176 int flags,
177 wxWindow* parent,
178 int x,
179 int y)
2bda0e17
KB
180
181{
3ca6a5f0
BP
182 wxFileDialog fileDialog(parent,
183 title ? title : wxT(""),
184 defaultDir ? defaultDir : wxT(""),
185 defaultFileName ? defaultFileName : wxT(""),
186 filter ? filter : wxT(""),
187 flags, wxPoint(x, y));
2bda0e17 188
3ca6a5f0 189 wxString filename;
e15e548b
VZ
190 if ( fileDialog.ShowModal() == wxID_OK )
191 {
3ca6a5f0
BP
192 if ( defaultFilterIndex )
193 *defaultFilterIndex = fileDialog.GetFilterIndex();
194
195 filename = fileDialog.GetPath();
e15e548b 196 }
3ca6a5f0
BP
197
198 return filename;
2bda0e17
KB
199}
200
2b5f62a0
VZ
201wxFileDialog::wxFileDialog(wxWindow *parent,
202 const wxString& message,
203 const wxString& defaultDir,
204 const wxString& defaultFileName,
205 const wxString& wildCard,
206 long style,
207 const wxPoint& WXUNUSED(pos))
2bda0e17
KB
208{
209 m_message = message;
210 m_dialogStyle = style;
c61f4f6d
VZ
211 if ( ( m_dialogStyle & wxMULTIPLE ) && ( m_dialogStyle & wxSAVE ) )
212 m_dialogStyle &= ~wxMULTIPLE;
2bda0e17 213 m_parent = parent;
223d09f6 214 m_path = wxT("");
e15e548b
VZ
215 m_fileName = defaultFileName;
216 m_dir = defaultDir;
217 m_wildCard = wildCard;
b922ef5a 218 m_filterIndex = 0;
2bda0e17
KB
219}
220
c61f4f6d
VZ
221void wxFileDialog::GetPaths(wxArrayString& paths) const
222{
223 paths.Empty();
224
225 wxString dir(m_dir);
226 if ( m_dir.Last() != _T('\\') )
227 dir += _T('\\');
228
229 size_t count = m_fileNames.GetCount();
230 for ( size_t n = 0; n < count; n++ )
231 {
232 paths.Add(dir + m_fileNames[n]);
233 }
234}
235
2b5f62a0
VZ
236void wxFileDialog::SetPath(const wxString& path)
237{
238 wxString ext;
239 wxSplitPath(path, &m_dir, &m_fileName, &ext);
240 if ( !ext.empty() )
241 m_fileName << _T('.') << ext;
242}
243
c61f4f6d 244int wxFileDialog::ShowModal()
2bda0e17 245{
1f2f0331
VZ
246 HWND hWnd = 0;
247 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
f6bcfd97
BP
248 if (!hWnd && wxTheApp->GetTopWindow())
249 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
2bda0e17 250
f6bcfd97
BP
251 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
252 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
2bda0e17 253
223d09f6
KB
254 *fileNameBuffer = wxT('\0');
255 *titleBuffer = wxT('\0');
2bda0e17 256
2bda0e17 257 long msw_flags = 0;
e15e548b 258 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
1f2f0331 259 msw_flags |= OFN_HIDEREADONLY;
e15e548b 260 if ( m_dialogStyle & wxFILE_MUST_EXIST )
1f2f0331 261 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
6e8aa701 262
c61f4f6d 263 if (m_dialogStyle & wxMULTIPLE )
6e8aa701
VZ
264 {
265 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
266 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
267 }
268
99d1b93d
VZ
269 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
270 // standard dialog does by default
6e8aa701
VZ
271 if ( !(m_dialogStyle & wxCHANGE_DIR) )
272 {
273 msw_flags |= OFN_NOCHANGEDIR;
274 }
012a01fc
JS
275/* chris elliott for some reason this does not work usefully if no extension
276 is given, as it test for junk instead of junk.ext
99d1b93d
VZ
277 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
278 {
279 msw_flags |= OFN_OVERWRITEPROMPT;
280 }
012a01fc 281*/
e15e548b 282 OPENFILENAME of;
f6bcfd97
BP
283 wxZeroMemory(of);
284
285 // the OPENFILENAME struct has been extended in newer version of
286 // comcdlg32.dll, but as we don't use the extended fields anyhow, set
287 // the struct size to the old value - otherwise, the programs compiled
288 // with new headers will not work with the old libraries
289#if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
290 of.lStructSize = sizeof(OPENFILENAME) -
291 (sizeof(void *) + 2*sizeof(DWORD));
292#else // old headers
e15e548b 293 of.lStructSize = sizeof(OPENFILENAME);
f6bcfd97
BP
294#endif
295
e15e548b 296 of.hwndOwner = hWnd;
837e5743 297 of.lpstrTitle = WXSTRINGCAST m_message;
e15e548b 298 of.lpstrFileTitle = titleBuffer;
f6bcfd97 299 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
2bda0e17 300
0bc9b25e 301 // Convert forward slashes to backslashes (file selector doesn't like
99d1b93d
VZ
302 // forward slashes) and also squeeze multiple consecutive slashes into one
303 // as it doesn't like two backslashes in a row neither
0627d091
RL
304
305 wxString dir;
306 size_t i, len = m_dir.length();
99d1b93d 307 dir.reserve(len);
0627d091 308 for ( i = 0; i < len; i++ )
99d1b93d
VZ
309 {
310 wxChar ch = m_dir[i];
311 switch ( ch )
312 {
313 case _T('/'):
314 // convert to backslash
315 ch = _T('\\');
316
317 // fall through
0bc9b25e 318
99d1b93d
VZ
319 case _T('\\'):
320 while ( i < len - 1 )
321 {
322 wxChar chNext = m_dir[i + 1];
323 if ( chNext != _T('\\') && chNext != _T('/') )
324 break;
325
04d93c3a
CE
326 // ignore the next one, unless it is at the start of a UNC path
327 if (i > 0)
328 i++;
329 else
330 break;
99d1b93d
VZ
331 }
332 // fall through
333
334 default:
335 // normal char
336 dir += ch;
337 }
338 }
339
340 of.lpstrInitialDir = dir.c_str();
2bda0e17 341
e15e548b 342 of.Flags = msw_flags;
2bda0e17
KB
343
344
2bda0e17
KB
345 //=== Like Alejandro Sierra's wildcard modification >>===================
346 /*
1f2f0331
VZ
347 In wxFileSelector you can put, instead of a single wild_card,
348 pairs of strings separated by '|'.
349 The first string is a description, and the
350 second is the wild card. You can put any number of pairs.
2bda0e17 351
1f2f0331 352 eg. "description1 (*.ex1)|*.ex1|description2 (*.ex2)|*.ex2"
2bda0e17 353
1f2f0331
VZ
354 If you put a single wild card, it works as before the modification.
355 */
2bda0e17
KB
356 //=======================================================================
357
4dba84be 358 wxString theFilter;
837e5743 359 if ( wxStrlen(m_wildCard) == 0 )
223d09f6 360 theFilter = wxString(wxT("*.*"));
4dba84be
JS
361 else
362 theFilter = m_wildCard ;
1f2f0331 363 wxString filterBuffer;
2bda0e17 364
223d09f6 365 if ( !wxStrchr( theFilter, wxT('|') ) ) { // only one filter ==> default text
1f2f0331
VZ
366 filterBuffer.Printf(_("Files (%s)|%s"),
367 theFilter.c_str(), theFilter.c_str());
e15e548b 368 }
1f2f0331
VZ
369 else { // more then one filter
370 filterBuffer = theFilter;
2bda0e17 371
574c0bbf
JS
372 }
373
223d09f6 374 filterBuffer += wxT("|");
574c0bbf 375 // Replace | with \0
0bc9b25e 376 for (i = 0; i < filterBuffer.Len(); i++ ) {
223d09f6
KB
377 if ( filterBuffer.GetChar(i) == wxT('|') ) {
378 filterBuffer[i] = wxT('\0');
e15e548b
VZ
379 }
380 }
2bda0e17 381
837e5743 382 of.lpstrFilter = (LPTSTR)(const wxChar *)filterBuffer;
cc42eb7a 383 of.nFilterIndex = m_filterIndex + 1;
2bda0e17
KB
384
385 //=== Setting defaultFileName >>=========================================
386
f6bcfd97
BP
387 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
388 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
2bda0e17 389
e15e548b 390 of.lpstrFile = fileNameBuffer; // holds returned filename
f6bcfd97 391 of.nMaxFile = wxMAXPATH;
2bda0e17
KB
392
393 //== Execute FileDialog >>=================================================
394
3f6638b8
VZ
395 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
396 : GetOpenFileName(&of)) != 0;
2bda0e17 397
f6bcfd97
BP
398 DWORD errCode = CommDlgExtendedError();
399
400#ifdef __WIN32__
401 if (!success && (errCode == CDERR_STRUCTSIZE))
402 {
403 // The struct size has changed so try a smaller or bigger size
404
405 int oldStructSize = of.lStructSize;
406 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
407 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
408 : (GetOpenFileName(&of) != 0);
409 errCode = CommDlgExtendedError();
410
411 if (!success && (errCode == CDERR_STRUCTSIZE))
412 {
413 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
414 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
415 : (GetOpenFileName(&of) != 0);
416 }
417 }
c6603ac2 418#endif // __WIN32__
f6bcfd97 419
2bda0e17
KB
420 if ( success )
421 {
c61f4f6d
VZ
422 m_fileNames.Empty();
423
424 if ( ( m_dialogStyle & wxMULTIPLE ) &&
425#if defined(OFN_EXPLORER)
426 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') ) )
427#else
428 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') ) )
429#endif // OFN_EXPLORER
430 {
431#if defined(OFN_EXPLORER)
432 m_dir = fileNameBuffer;
433 i = of.nFileOffset;
434 m_fileName = &fileNameBuffer[i];
435 m_fileNames.Add(m_fileName);
436 i += m_fileName.Len() + 1;
437
438 while (fileNameBuffer[i] != wxT('\0'))
439 {
440 m_fileNames.Add(&fileNameBuffer[i]);
441 i += wxStrlen(&fileNameBuffer[i]) + 1;
442 }
443#else
c6603ac2 444 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
c61f4f6d
VZ
445 m_dir = toke.GetNextToken();
446 m_fileName = toke.GetNextToken();
447 m_fileNames.Add(m_fileName);
448
449 while (toke.HasMoreTokens())
450 m_fileNames.Add(toke.GetNextToken());
451#endif // OFN_EXPLORER
452
453 wxString dir(m_dir);
454 if ( m_dir.Last() != _T('\\') )
455 dir += _T('\\');
456
457 m_fileNames.Sort();
458 m_path = dir + m_fileName;
459 }
460 else
461 {
462 const wxChar* extension = NULL;
1f2f0331 463
c61f4f6d 464 //=== Adding the correct extension >>=================================
2bda0e17 465
cc42eb7a 466 m_filterIndex = (int)of.nFilterIndex - 1;
2bda0e17 467
c6603ac2
VS
468 if ( !of.nFileExtension ||
469 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
470 {
471 // User has typed a filename without an extension:
2bda0e17 472
a039ccbf
VS
473 // A filename can end in a "." here ("abc."), this means it
474 // does not have an extension. Because later on a "." with
475 // the default extension is appended we remove the "." if
476 // filename ends with one (We don't want files called
477 // "abc..ext")
478 int idx = wxStrlen(fileNameBuffer) - 1;
479 if ( fileNameBuffer[idx] == wxT('.') )
480 {
481 fileNameBuffer[idx] = wxT('\0');
482 }
483
c61f4f6d
VZ
484 int maxFilter = (int)(of.nFilterIndex*2L-1L);
485 extension = filterBuffer;
2bda0e17 486
c61f4f6d
VZ
487 for( int i = 0; i < maxFilter; i++ ) { // get extension
488 extension = extension + wxStrlen( extension ) +1;
489 }
2bda0e17 490
c61f4f6d
VZ
491 extension = wxStrrchr( extension, wxT('.') );
492 if ( extension // != "blabla"
493 && !wxStrrchr( extension, wxT('*') ) // != "blabla.*"
494 && !wxStrrchr( extension, wxT('?') ) // != "blabla.?"
495 && extension[1] // != "blabla."
496 && extension[1] != wxT(' ') ) // != "blabla. "
497 {
498 // now concat extension to the fileName:
499 m_fileName = wxString(fileNameBuffer) + extension;
2bda0e17 500
c61f4f6d 501 int len = wxStrlen( fileNameBuffer );
f6bcfd97
BP
502 wxStrncpy( fileNameBuffer + len, extension, wxMAXPATH - len );
503 fileNameBuffer[ wxMAXPATH -1 ] = wxT('\0');
c61f4f6d 504 }
2bda0e17 505 }
2bda0e17 506
c61f4f6d
VZ
507 m_path = fileNameBuffer;
508 m_fileName = wxFileNameFromPath(fileNameBuffer);
509 m_fileNames.Add(m_fileName);
510 m_dir = wxPathOnly(fileNameBuffer);
511 }
012a01fc
JS
512 //=== Simulating the wxOVERWRITE_PROMPT >>============================
513 //should we also test for file save style ??
514 if ( (m_dialogStyle & wxOVERWRITE_PROMPT) &&
515 ::wxFileExists( fileNameBuffer ) )
516 {
517 wxString messageText;
518 messageText.Printf(_("File '%s' already exists.\nDo you want to replace it?"), fileNameBuffer);
519 if ( wxMessageBox(messageText, wxT("Save File As"), wxYES_NO | wxICON_EXCLAMATION ) != wxYES )
520 {
521 success = FALSE;
522 }
523 }
7cc98b3e
VZ
524 }
525 else
526 {
527 // common dialog failed - why?
528#ifdef __WXDEBUG__
529 DWORD dwErr = CommDlgExtendedError();
530 if ( dwErr != 0 )
531 {
532 // this msg is only for developers
223d09f6 533 wxLogError(wxT("Common dialog failed with error code %0lx."),
7cc98b3e
VZ
534 dwErr);
535 }
536 //else: it was just cancelled
537#endif
538 }
2bda0e17 539
7cc98b3e 540 return success ? wxID_OK : wxID_CANCEL;
2bda0e17
KB
541
542}
543
ba681060
VZ
544// Generic file load/save dialog (for internal use only)
545static
546wxString wxDefaultFileSelector(bool load,
837e5743
OK
547 const wxChar *what,
548 const wxChar *extension,
549 const wxChar *default_name,
ba681060 550 wxWindow *parent)
2bda0e17 551{
b121fa31
VZ
552 wxString prompt;
553 wxString str;
554 if (load)
555 str = _("Load %s file");
556 else
557 str = _("Save %s file");
558 prompt.Printf(str, what);
1f2f0331 559
b121fa31
VZ
560 const wxChar *ext = extension;
561 if (*ext == wxT('.'))
562 ext++;
2bda0e17 563
b121fa31
VZ
564 wxString wild;
565 wild.Printf(wxT("*.%s"), ext);
2bda0e17 566
b121fa31
VZ
567 return wxFileSelector(prompt, NULL, default_name, ext, wild,
568 load ? wxOPEN : wxSAVE, parent);
2bda0e17
KB
569}
570
571// Generic file load dialog
837e5743
OK
572WXDLLEXPORT wxString wxLoadFileSelector(const wxChar *what,
573 const wxChar *extension,
574 const wxChar *default_name,
ba681060 575 wxWindow *parent)
2bda0e17 576{
ba681060 577 return wxDefaultFileSelector(TRUE, what, extension, default_name, parent);
2bda0e17
KB
578}
579
2bda0e17 580// Generic file save dialog
837e5743
OK
581WXDLLEXPORT wxString wxSaveFileSelector(const wxChar *what,
582 const wxChar *extension,
583 const wxChar *default_name,
ba681060 584 wxWindow *parent)
2bda0e17 585{
ba681060 586 return wxDefaultFileSelector(FALSE, what, extension, default_name, parent);
2bda0e17
KB
587}
588
1e6feb95 589#endif // wxUSE_FILEDLG
c61f4f6d 590