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__))
29 #include "wx/filedlg.h"
33 #include "wx/msgdlg.h"
34 #include "wx/filefn.h"
41 #include "wx/msw/wrapcdlg.h"
46 #include "wx/filename.h"
47 #include "wx/tokenzr.h"
49 #include "wx/msw/missing.h"
51 // ----------------------------------------------------------------------------
53 // ----------------------------------------------------------------------------
56 # define wxMAXPATH 65534
58 # define wxMAXPATH 1024
61 # define wxMAXFILE 1024
65 // ----------------------------------------------------------------------------
67 // ----------------------------------------------------------------------------
69 // standard dialog size
70 static wxRect
gs_rectDialog(0, 0, 428, 266);
72 // ============================================================================
74 // ============================================================================
76 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
)
78 // ----------------------------------------------------------------------------
79 // hook function for moving the dialog
80 // ----------------------------------------------------------------------------
83 wxFileDialogHookFunction(HWND hDlg
,
85 WPARAM
WXUNUSED(wParam
),
92 OFNOTIFY
*pNotifyCode
= wx_reinterpret_cast(OFNOTIFY
*, lParam
);
93 if ( pNotifyCode
->hdr
.code
== CDN_INITDONE
)
95 // note that we need to move the parent window: hDlg is a
96 // child of it when OFN_EXPLORER is used
101 gs_rectDialog
.x
, gs_rectDialog
.y
,
103 SWP_NOZORDER
| SWP_NOSIZE
110 // reuse the position used for the dialog the next time by default
112 // NB: at least under Windows 2003 this is useless as after the
113 // first time it's shown the dialog always remembers its size
114 // and position itself and ignores any later SetWindowPos calls
115 wxCopyRECTToRect(wxGetWindowRect(::GetParent(hDlg
)), gs_rectDialog
);
119 // do the default processing
123 // ----------------------------------------------------------------------------
125 // ----------------------------------------------------------------------------
127 wxFileDialog::wxFileDialog(wxWindow
*parent
,
128 const wxString
& message
,
129 const wxString
& defaultDir
,
130 const wxString
& defaultFileName
,
131 const wxString
& wildCard
,
135 const wxString
& name
)
136 : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
,
137 wildCard
, style
, pos
, sz
, name
)
140 // NB: all style checks are done by wxFileDialogBase::Create
142 m_bMovedWindow
= false;
144 // Must set to zero, otherwise the wx routines won't size the window
145 // the second time you call the file dialog, because it thinks it is
146 // already at the requested size.. (when centering)
151 void wxFileDialog::GetPaths(wxArrayString
& paths
) const
156 if ( m_dir
.Last() != _T('\\') )
159 size_t count
= m_fileNames
.GetCount();
160 for ( size_t n
= 0; n
< count
; n
++ )
162 if (wxFileName(m_fileNames
[n
]).IsAbsolute())
163 paths
.Add(m_fileNames
[n
]);
165 paths
.Add(dir
+ m_fileNames
[n
]);
169 void wxFileDialog::GetFilenames(wxArrayString
& files
) const
174 void wxFileDialog::SetPath(const wxString
& path
)
177 wxSplitPath(path
, &m_dir
, &m_fileName
, &ext
);
179 m_fileName
<< _T('.') << ext
;
182 void wxFileDialog::DoGetPosition(int *x
, int *y
) const
185 *x
= gs_rectDialog
.x
;
187 *y
= gs_rectDialog
.y
;
191 void wxFileDialog::DoGetSize(int *width
, int *height
) const
194 *width
= gs_rectDialog
.width
;
196 *height
= gs_rectDialog
.height
;
199 void wxFileDialog::DoMoveWindow(int x
, int y
, int WXUNUSED(w
), int WXUNUSED(h
))
201 m_bMovedWindow
= true;
206 // size of the dialog can't be changed because the controls are not laid
207 // out correctly then
210 // helper used below in ShowModal(): style is used to determine whether to show
211 // the "Save file" dialog (if it contains wxFD_SAVE bit) or "Open file" one;
212 // returns true on success or false on failure in which case err is filled with
213 // the CDERR_XXX constant
214 static bool DoShowCommFileDialog(OPENFILENAME
*of
, long style
, DWORD
*err
)
216 if ( style
& wxFD_SAVE
? GetSaveFileName(of
) : GetOpenFileName(of
) )
222 // according to MSDN, CommDlgExtendedError() should work under CE as
223 // well but apparently in practice it doesn't (anybody has more
225 *err
= GetLastError();
227 *err
= CommDlgExtendedError();
234 // We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
235 // know if the OPENFILENAME declared in the currently used headers is a V5 or
236 // V4 (smaller) one so we try to manually extend the struct in case it is the
239 // We don't do this on Windows CE nor under Win64, however, as there are no
240 // compilers with old headers for these architectures
241 #if defined(__WXWINCE__) || defined(__WIN64__)
242 typedef OPENFILENAME wxOPENFILENAME
;
244 static const DWORD gs_ofStructSize
= sizeof(OPENFILENAME
);
245 #else // !__WXWINCE__ || __WIN64__
246 #define wxTRY_SMALLER_OPENFILENAME
248 struct wxOPENFILENAME
: public OPENFILENAME
250 // fields added in Windows 2000/XP comdlg32.dll version
256 // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
257 // because sizeof(OPENFILENAME) in the headers we use when compiling the
258 // library could be less if _WIN32_WINNT is not >= 0x500
259 static const DWORD wxOPENFILENAME_V5_SIZE
= 88;
261 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
262 static const DWORD wxOPENFILENAME_V4_SIZE
= 76;
264 // always try the new one first
265 static DWORD gs_ofStructSize
= wxOPENFILENAME_V5_SIZE
;
266 #endif // __WXWINCE__ || __WIN64__/!...
268 int wxFileDialog::ShowModal()
271 if (m_parent
) hWnd
= (HWND
) m_parent
->GetHWND();
272 if (!hWnd
&& wxTheApp
->GetTopWindow())
273 hWnd
= (HWND
) wxTheApp
->GetTopWindow()->GetHWND();
275 static wxChar fileNameBuffer
[ wxMAXPATH
]; // the file-name
276 wxChar titleBuffer
[ wxMAXFILE
+1+wxMAXEXT
]; // the file-name, without path
278 *fileNameBuffer
= wxT('\0');
279 *titleBuffer
= wxT('\0');
281 #if WXWIN_COMPATIBILITY_2_4
283 if ( (m_windowStyle
& wxHIDE_READONLY
) || (m_windowStyle
& wxFD_SAVE
) )
284 msw_flags
|= OFN_HIDEREADONLY
;
286 long msw_flags
= OFN_HIDEREADONLY
;
289 if ( m_windowStyle
& wxFD_FILE_MUST_EXIST
)
290 msw_flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
292 If the window has been moved the programmer is probably
293 trying to center or position it. Thus we set the callback
294 or hook function so that we can actually adjust the position.
295 Without moving or centering the dlg, it will just stay
296 in the upper left of the frame, it does not center
299 if (m_bMovedWindow
) // we need these flags.
301 msw_flags
|= OFN_EXPLORER
|OFN_ENABLEHOOK
;
303 msw_flags
|= OFN_ENABLESIZING
;
307 if (m_windowStyle
& wxFD_MULTIPLE
)
309 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
310 msw_flags
|= OFN_EXPLORER
| OFN_ALLOWMULTISELECT
;
313 // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the
314 // standard dialog does by default (notice that under NT it does it anyhow,
315 // OFN_NOCHANGEDIR or not, see below)
316 if ( !(m_windowStyle
& wxFD_CHANGE_DIR
) )
318 msw_flags
|= OFN_NOCHANGEDIR
;
321 if ( m_windowStyle
& wxFD_OVERWRITE_PROMPT
)
323 msw_flags
|= OFN_OVERWRITEPROMPT
;
329 of
.lStructSize
= gs_ofStructSize
;
331 of
.lpstrTitle
= WXSTRINGCAST m_message
;
332 of
.lpstrFileTitle
= titleBuffer
;
333 of
.nMaxFileTitle
= wxMAXFILE
+ 1 + wxMAXEXT
;
335 // Convert forward slashes to backslashes (file selector doesn't like
336 // forward slashes) and also squeeze multiple consecutive slashes into one
337 // as it doesn't like two backslashes in a row neither
340 size_t i
, len
= m_dir
.length();
342 for ( i
= 0; i
< len
; i
++ )
344 wxChar ch
= m_dir
[i
];
348 // convert to backslash
354 while ( i
< len
- 1 )
356 wxChar chNext
= m_dir
[i
+ 1];
357 if ( chNext
!= _T('\\') && chNext
!= _T('/') )
360 // ignore the next one, unless it is at the start of a UNC path
374 of
.lpstrInitialDir
= dir
.c_str();
376 of
.Flags
= msw_flags
;
377 of
.lpfnHook
= wxFileDialogHookFunction
;
379 wxArrayString wildDescriptions
, wildFilters
;
381 size_t items
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
);
383 wxASSERT_MSG( items
> 0 , _T("empty wildcard list") );
385 wxString filterBuffer
;
387 for (i
= 0; i
< items
; i
++)
389 filterBuffer
+= wildDescriptions
[i
];
390 filterBuffer
+= wxT("|");
391 filterBuffer
+= wildFilters
[i
];
392 filterBuffer
+= wxT("|");
396 for (i
= 0; i
< filterBuffer
.length(); i
++ ) {
397 if ( filterBuffer
.GetChar(i
) == wxT('|') ) {
398 filterBuffer
[i
] = wxT('\0');
402 of
.lpstrFilter
= (LPTSTR
)filterBuffer
.c_str();
403 of
.nFilterIndex
= m_filterIndex
+ 1;
405 //=== Setting defaultFileName >>=========================================
407 wxStrncpy( fileNameBuffer
, (const wxChar
*)m_fileName
, wxMAXPATH
-1 );
408 fileNameBuffer
[ wxMAXPATH
-1 ] = wxT('\0');
410 of
.lpstrFile
= fileNameBuffer
; // holds returned filename
411 of
.nMaxFile
= wxMAXPATH
;
413 // we must set the default extension because otherwise Windows would check
414 // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the
415 // user types "foo" and the default extension is ".bar" we should force it
416 // to check for "foo.bar" existence and not "foo")
417 wxString defextBuffer
; // we need it to be alive until GetSaveFileName()!
418 if (m_windowStyle
& wxFD_SAVE
)
420 const wxChar
* extension
= filterBuffer
;
421 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
423 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
424 extension
= extension
+ wxStrlen( extension
) + 1;
426 // use dummy name a to avoid assert in AppendExtension
427 defextBuffer
= AppendExtension(wxT("a"), extension
);
428 if (defextBuffer
.StartsWith(wxT("a.")))
431 of
.lpstrDefExt
= defextBuffer
.c_str();
435 // store off before the standard windows dialog can possibly change it
436 const wxString cwdOrig
= wxGetCwd();
438 //== Execute FileDialog >>=================================================
441 bool success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
443 #ifdef wxTRY_SMALLER_OPENFILENAME
444 // the system might be too old to support the new version file dialog
445 // boxes, try with the old size
446 if ( !success
&& errCode
== CDERR_STRUCTSIZE
&&
447 of
.lStructSize
!= wxOPENFILENAME_V4_SIZE
)
449 of
.lStructSize
= wxOPENFILENAME_V4_SIZE
;
451 success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
453 if ( success
|| !errCode
)
455 // use this struct size for subsequent dialogs
456 gs_ofStructSize
= of
.lStructSize
;
459 #endif // wxTRY_SMALLER_OPENFILENAME
463 // GetOpenFileName will always change the current working directory on
464 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
465 // OFN_NOCHANGEDIR has no effect. If the user did not specify
466 // wxFD_CHANGE_DIR let's restore the current working directory to what it
467 // was before the dialog was shown.
468 if ( msw_flags
& OFN_NOCHANGEDIR
)
470 wxSetWorkingDirectory(cwdOrig
);
475 if ( ( m_windowStyle
& wxFD_MULTIPLE
) &&
476 #if defined(OFN_EXPLORER)
477 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') )
479 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') )
480 #endif // OFN_EXPLORER
483 #if defined(OFN_EXPLORER)
484 m_dir
= fileNameBuffer
;
486 m_fileName
= &fileNameBuffer
[i
];
487 m_fileNames
.Add(m_fileName
);
488 i
+= m_fileName
.length() + 1;
490 while (fileNameBuffer
[i
] != wxT('\0'))
492 m_fileNames
.Add(&fileNameBuffer
[i
]);
493 i
+= wxStrlen(&fileNameBuffer
[i
]) + 1;
496 wxStringTokenizer
toke(fileNameBuffer
, _T(" \t\r\n"));
497 m_dir
= toke
.GetNextToken();
498 m_fileName
= toke
.GetNextToken();
499 m_fileNames
.Add(m_fileName
);
501 while (toke
.HasMoreTokens())
502 m_fileNames
.Add(toke
.GetNextToken());
503 #endif // OFN_EXPLORER
506 if ( m_dir
.Last() != _T('\\') )
509 m_path
= dir
+ m_fileName
;
510 m_filterIndex
= (int)of
.nFilterIndex
- 1;
514 //=== Adding the correct extension >>=================================
516 m_filterIndex
= (int)of
.nFilterIndex
- 1;
518 if ( !of
.nFileExtension
||
519 (of
.nFileExtension
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) )
521 // User has typed a filename without an extension:
522 const wxChar
* extension
= filterBuffer
;
523 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
525 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
526 extension
= extension
+ wxStrlen( extension
) + 1;
528 m_fileName
= AppendExtension(fileNameBuffer
, extension
);
529 wxStrncpy(fileNameBuffer
, m_fileName
.c_str(), wxMin(m_fileName
.length(), wxMAXPATH
-1));
530 fileNameBuffer
[wxMin(m_fileName
.length(), wxMAXPATH
-1)] = wxT('\0');
533 m_path
= fileNameBuffer
;
534 m_fileName
= wxFileNameFromPath(fileNameBuffer
);
535 m_fileNames
.Add(m_fileName
);
536 m_dir
= wxPathOnly(fileNameBuffer
);
542 // common dialog failed - why?
545 // this msg is only for developers so don't translate it
546 wxLogError(wxT("Common dialog failed with error code %0lx."),
549 //else: it was just cancelled
551 #endif // __WXDEBUG__
553 return success
? wxID_OK
: wxID_CANCEL
;
557 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)