1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Julian Smart
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
12 // ============================================================================
14 // ============================================================================
16 // ----------------------------------------------------------------------------
18 // ----------------------------------------------------------------------------
20 // For compilers that support precompilation, includes "wx.h".
21 #include "wx/wxprec.h"
27 #if wxUSE_FILEDLG && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
31 #include "wx/msgdlg.h"
32 #include "wx/filedlg.h"
33 #include "wx/filefn.h"
39 #include "wx/msw/wrapcdlg.h"
44 #include "wx/filename.h"
45 #include "wx/tokenzr.h"
48 #include "wx/msw/missing.h"
50 // ----------------------------------------------------------------------------
52 // ----------------------------------------------------------------------------
55 # define wxMAXPATH 65534
57 # define wxMAXPATH 1024
60 # define wxMAXFILE 1024
64 // ----------------------------------------------------------------------------
66 // ----------------------------------------------------------------------------
68 // standard dialog size
69 static wxRect
gs_rectDialog(0, 0, 428, 266);
71 // ============================================================================
73 // ============================================================================
75 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
)
77 // ----------------------------------------------------------------------------
78 // hook function for moving the dialog
79 // ----------------------------------------------------------------------------
82 wxFileDialogHookFunction(HWND hDlg
,
84 WPARAM
WXUNUSED(wParam
),
91 OFNOTIFY
*pNotifyCode
= wx_reinterpret_cast(OFNOTIFY
*, lParam
);
92 if ( pNotifyCode
->hdr
.code
== CDN_INITDONE
)
94 // note that we need to move the parent window: hDlg is a
95 // child of it when OFN_EXPLORER is used
100 gs_rectDialog
.x
, gs_rectDialog
.y
,
102 SWP_NOZORDER
| SWP_NOSIZE
109 // reuse the position used for the dialog the next time by default
111 // NB: at least under Windows 2003 this is useless as after the
112 // first time it's shown the dialog always remembers its size
113 // and position itself and ignores any later SetWindowPos calls
114 wxCopyRECTToRect(wxGetWindowRect(::GetParent(hDlg
)), gs_rectDialog
);
118 // do the default processing
122 // ----------------------------------------------------------------------------
124 // ----------------------------------------------------------------------------
126 wxFileDialog::wxFileDialog(wxWindow
*parent
,
127 const wxString
& message
,
128 const wxString
& defaultDir
,
129 const wxString
& defaultFileName
,
130 const wxString
& wildCard
,
134 const wxString
& name
)
135 : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
,
136 wildCard
, style
, pos
, sz
, name
)
139 // NB: all style checks are done by wxFileDialogBase::Create
141 m_bMovedWindow
= false;
143 // Must set to zero, otherwise the wx routines won't size the window
144 // the second time you call the file dialog, because it thinks it is
145 // already at the requested size.. (when centering)
150 void wxFileDialog::GetPaths(wxArrayString
& paths
) const
155 if ( m_dir
.Last() != _T('\\') )
158 size_t count
= m_fileNames
.GetCount();
159 for ( size_t n
= 0; n
< count
; n
++ )
161 if (wxFileName(m_fileNames
[n
]).IsAbsolute())
162 paths
.Add(m_fileNames
[n
]);
164 paths
.Add(dir
+ m_fileNames
[n
]);
168 void wxFileDialog::GetFilenames(wxArrayString
& files
) const
173 void wxFileDialog::SetPath(const wxString
& path
)
176 wxSplitPath(path
, &m_dir
, &m_fileName
, &ext
);
178 m_fileName
<< _T('.') << ext
;
181 void wxFileDialog::DoGetPosition(int *x
, int *y
) const
184 *x
= gs_rectDialog
.x
;
186 *y
= gs_rectDialog
.y
;
190 void wxFileDialog::DoGetSize(int *width
, int *height
) const
193 *width
= gs_rectDialog
.width
;
195 *height
= gs_rectDialog
.height
;
198 void wxFileDialog::DoMoveWindow(int x
, int y
, int WXUNUSED(w
), int WXUNUSED(h
))
200 m_bMovedWindow
= true;
205 // size of the dialog can't be changed because the controls are not laid
206 // out correctly then
209 // helper used below in ShowModal(): style is used to determine whether to show
210 // the "Save file" dialog (if it contains wxFD_SAVE bit) or "Open file" one;
211 // returns true on success or false on failure in which case err is filled with
212 // the CDERR_XXX constant
213 static bool DoShowCommFileDialog(OPENFILENAME
*of
, long style
, DWORD
*err
)
215 if ( style
& wxFD_SAVE
? GetSaveFileName(of
) : GetOpenFileName(of
) )
221 // according to MSDN, CommDlgExtendedError() should work under CE as
222 // well but apparently in practice it doesn't (anybody has more
224 *err
= GetLastError();
226 *err
= CommDlgExtendedError();
233 // We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
234 // know if the OPENFILENAME declared in the currently used headers is a V5 or
235 // V4 (smaller) one so we try to manually extend the struct in case it is the
238 // We don't do this on Windows CE nor under Win64, however, as there are no
239 // compilers with old headers for these architectures
240 #if defined(__WXWINCE__) || defined(__WIN64__)
241 typedef OPENFILENAME wxOPENFILENAME
;
243 static const DWORD gs_ofStructSize
= sizeof(OPENFILENAME
);
244 #else // !__WXWINCE__ || __WIN64__
245 #define wxTRY_SMALLER_OPENFILENAME
247 struct wxOPENFILENAME
: public OPENFILENAME
249 // fields added in Windows 2000/XP comdlg32.dll version
255 // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
256 // because sizeof(OPENFILENAME) in the headers we use when compiling the
257 // library could be less if _WIN32_WINNT is not >= 0x500
258 static const DWORD wxOPENFILENAME_V5_SIZE
= 88;
260 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
261 static const DWORD wxOPENFILENAME_V4_SIZE
= 76;
263 // always try the new one first
264 static DWORD gs_ofStructSize
= wxOPENFILENAME_V5_SIZE
;
265 #endif // __WXWINCE__ || __WIN64__/!...
267 int wxFileDialog::ShowModal()
270 if (m_parent
) hWnd
= (HWND
) m_parent
->GetHWND();
271 if (!hWnd
&& wxTheApp
->GetTopWindow())
272 hWnd
= (HWND
) wxTheApp
->GetTopWindow()->GetHWND();
274 static wxChar fileNameBuffer
[ wxMAXPATH
]; // the file-name
275 wxChar titleBuffer
[ wxMAXFILE
+1+wxMAXEXT
]; // the file-name, without path
277 *fileNameBuffer
= wxT('\0');
278 *titleBuffer
= wxT('\0');
280 #if WXWIN_COMPATIBILITY_2_4
282 if ( (m_windowStyle
& wxHIDE_READONLY
) || (m_windowStyle
& wxFD_SAVE
) )
283 msw_flags
|= OFN_HIDEREADONLY
;
285 long msw_flags
= OFN_HIDEREADONLY
;
288 if ( m_windowStyle
& wxFD_FILE_MUST_EXIST
)
289 msw_flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
291 If the window has been moved the programmer is probably
292 trying to center or position it. Thus we set the callback
293 or hook function so that we can actually adjust the position.
294 Without moving or centering the dlg, it will just stay
295 in the upper left of the frame, it does not center
298 if (m_bMovedWindow
) // we need these flags.
300 msw_flags
|= OFN_EXPLORER
|OFN_ENABLEHOOK
;
302 msw_flags
|= OFN_ENABLESIZING
;
306 if (m_windowStyle
& wxFD_MULTIPLE
)
308 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
309 msw_flags
|= OFN_EXPLORER
| OFN_ALLOWMULTISELECT
;
312 // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the
313 // standard dialog does by default (notice that under NT it does it anyhow,
314 // OFN_NOCHANGEDIR or not, see below)
315 if ( !(m_windowStyle
& wxFD_CHANGE_DIR
) )
317 msw_flags
|= OFN_NOCHANGEDIR
;
320 if ( m_windowStyle
& wxFD_OVERWRITE_PROMPT
)
322 msw_flags
|= OFN_OVERWRITEPROMPT
;
328 of
.lStructSize
= gs_ofStructSize
;
330 of
.lpstrTitle
= WXSTRINGCAST m_message
;
331 of
.lpstrFileTitle
= titleBuffer
;
332 of
.nMaxFileTitle
= wxMAXFILE
+ 1 + wxMAXEXT
;
334 // Convert forward slashes to backslashes (file selector doesn't like
335 // forward slashes) and also squeeze multiple consecutive slashes into one
336 // as it doesn't like two backslashes in a row neither
339 size_t i
, len
= m_dir
.length();
341 for ( i
= 0; i
< len
; i
++ )
343 wxChar ch
= m_dir
[i
];
347 // convert to backslash
353 while ( i
< len
- 1 )
355 wxChar chNext
= m_dir
[i
+ 1];
356 if ( chNext
!= _T('\\') && chNext
!= _T('/') )
359 // ignore the next one, unless it is at the start of a UNC path
373 of
.lpstrInitialDir
= dir
.c_str();
375 of
.Flags
= msw_flags
;
376 of
.lpfnHook
= wxFileDialogHookFunction
;
378 wxArrayString wildDescriptions
, wildFilters
;
380 size_t items
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
);
382 wxASSERT_MSG( items
> 0 , _T("empty wildcard list") );
384 wxString filterBuffer
;
386 for (i
= 0; i
< items
; i
++)
388 filterBuffer
+= wildDescriptions
[i
];
389 filterBuffer
+= wxT("|");
390 filterBuffer
+= wildFilters
[i
];
391 filterBuffer
+= wxT("|");
395 for (i
= 0; i
< filterBuffer
.length(); i
++ ) {
396 if ( filterBuffer
.GetChar(i
) == wxT('|') ) {
397 filterBuffer
[i
] = wxT('\0');
401 of
.lpstrFilter
= (LPTSTR
)filterBuffer
.c_str();
402 of
.nFilterIndex
= m_filterIndex
+ 1;
404 //=== Setting defaultFileName >>=========================================
406 wxStrncpy( fileNameBuffer
, (const wxChar
*)m_fileName
, wxMAXPATH
-1 );
407 fileNameBuffer
[ wxMAXPATH
-1 ] = wxT('\0');
409 of
.lpstrFile
= fileNameBuffer
; // holds returned filename
410 of
.nMaxFile
= wxMAXPATH
;
412 // we must set the default extension because otherwise Windows would check
413 // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the
414 // user types "foo" and the default extension is ".bar" we should force it
415 // to check for "foo.bar" existence and not "foo")
416 wxString defextBuffer
; // we need it to be alive until GetSaveFileName()!
417 if (m_windowStyle
& wxFD_SAVE
)
419 const wxChar
* extension
= filterBuffer
;
420 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
422 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
423 extension
= extension
+ wxStrlen( extension
) + 1;
425 // use dummy name a to avoid assert in AppendExtension
426 defextBuffer
= AppendExtension(wxT("a"), extension
);
427 if (defextBuffer
.StartsWith(wxT("a.")))
430 of
.lpstrDefExt
= defextBuffer
.c_str();
434 // store off before the standard windows dialog can possibly change it
435 const wxString cwdOrig
= wxGetCwd();
437 //== Execute FileDialog >>=================================================
440 bool success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
442 #ifdef wxTRY_SMALLER_OPENFILENAME
443 // the system might be too old to support the new version file dialog
444 // boxes, try with the old size
445 if ( !success
&& errCode
== CDERR_STRUCTSIZE
&&
446 of
.lStructSize
!= wxOPENFILENAME_V4_SIZE
)
448 of
.lStructSize
= wxOPENFILENAME_V4_SIZE
;
450 success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
452 if ( success
|| !errCode
)
454 // use this struct size for subsequent dialogs
455 gs_ofStructSize
= of
.lStructSize
;
458 #endif // wxTRY_SMALLER_OPENFILENAME
462 // GetOpenFileName will always change the current working directory on
463 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
464 // OFN_NOCHANGEDIR has no effect. If the user did not specify
465 // wxFD_CHANGE_DIR let's restore the current working directory to what it
466 // was before the dialog was shown.
467 if ( msw_flags
& OFN_NOCHANGEDIR
)
469 wxSetWorkingDirectory(cwdOrig
);
474 if ( ( m_windowStyle
& wxFD_MULTIPLE
) &&
475 #if defined(OFN_EXPLORER)
476 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') )
478 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') )
479 #endif // OFN_EXPLORER
482 #if defined(OFN_EXPLORER)
483 m_dir
= fileNameBuffer
;
485 m_fileName
= &fileNameBuffer
[i
];
486 m_fileNames
.Add(m_fileName
);
487 i
+= m_fileName
.length() + 1;
489 while (fileNameBuffer
[i
] != wxT('\0'))
491 m_fileNames
.Add(&fileNameBuffer
[i
]);
492 i
+= wxStrlen(&fileNameBuffer
[i
]) + 1;
495 wxStringTokenizer
toke(fileNameBuffer
, _T(" \t\r\n"));
496 m_dir
= toke
.GetNextToken();
497 m_fileName
= toke
.GetNextToken();
498 m_fileNames
.Add(m_fileName
);
500 while (toke
.HasMoreTokens())
501 m_fileNames
.Add(toke
.GetNextToken());
502 #endif // OFN_EXPLORER
505 if ( m_dir
.Last() != _T('\\') )
508 m_path
= dir
+ m_fileName
;
509 m_filterIndex
= (int)of
.nFilterIndex
- 1;
513 //=== Adding the correct extension >>=================================
515 m_filterIndex
= (int)of
.nFilterIndex
- 1;
517 if ( !of
.nFileExtension
||
518 (of
.nFileExtension
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) )
520 // User has typed a filename without an extension:
521 const wxChar
* extension
= filterBuffer
;
522 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
524 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
525 extension
= extension
+ wxStrlen( extension
) + 1;
527 m_fileName
= AppendExtension(fileNameBuffer
, extension
);
528 wxStrncpy(fileNameBuffer
, m_fileName
.c_str(), wxMin(m_fileName
.length(), wxMAXPATH
-1));
529 fileNameBuffer
[wxMin(m_fileName
.length(), wxMAXPATH
-1)] = wxT('\0');
532 m_path
= fileNameBuffer
;
533 m_fileName
= wxFileNameFromPath(fileNameBuffer
);
534 m_fileNames
.Add(m_fileName
);
535 m_dir
= wxPathOnly(fileNameBuffer
);
541 // common dialog failed - why?
544 // this msg is only for developers so don't translate it
545 wxLogError(wxT("Common dialog failed with error code %0lx."),
548 //else: it was just cancelled
550 #endif // __WXDEBUG__
552 return success
? wxID_OK
: wxID_CANCEL
;
556 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)