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"
32 #include "wx/msw/wrapcdlg.h"
33 #include "wx/msw/missing.h"
35 #include "wx/msgdlg.h"
36 #include "wx/filefn.h"
46 #include "wx/filename.h"
47 #include "wx/tokenzr.h"
49 // ----------------------------------------------------------------------------
51 // ----------------------------------------------------------------------------
54 # define wxMAXPATH 65534
56 # define wxMAXPATH 1024
59 # define wxMAXFILE 1024
63 // ----------------------------------------------------------------------------
65 // ----------------------------------------------------------------------------
67 // standard dialog size
68 static wxRect
gs_rectDialog(0, 0, 428, 266);
70 // ============================================================================
72 // ============================================================================
74 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
)
76 // ----------------------------------------------------------------------------
77 // hook function for moving the dialog
78 // ----------------------------------------------------------------------------
81 wxFileDialogHookFunction(HWND hDlg
,
83 WPARAM
WXUNUSED(wParam
),
90 OFNOTIFY
*pNotifyCode
= wx_reinterpret_cast(OFNOTIFY
*, lParam
);
91 if ( pNotifyCode
->hdr
.code
== CDN_INITDONE
)
93 // note that we need to move the parent window: hDlg is a
94 // child of it when OFN_EXPLORER is used
99 gs_rectDialog
.x
, gs_rectDialog
.y
,
101 SWP_NOZORDER
| SWP_NOSIZE
108 // reuse the position used for the dialog the next time by default
110 // NB: at least under Windows 2003 this is useless as after the
111 // first time it's shown the dialog always remembers its size
112 // and position itself and ignores any later SetWindowPos calls
113 wxCopyRECTToRect(wxGetWindowRect(::GetParent(hDlg
)), gs_rectDialog
);
117 // do the default processing
121 // ----------------------------------------------------------------------------
123 // ----------------------------------------------------------------------------
125 wxFileDialog::wxFileDialog(wxWindow
*parent
,
126 const wxString
& message
,
127 const wxString
& defaultDir
,
128 const wxString
& defaultFileName
,
129 const wxString
& wildCard
,
133 const wxString
& name
)
134 : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
,
135 wildCard
, style
, pos
, sz
, name
)
138 // NB: all style checks are done by wxFileDialogBase::Create
140 m_bMovedWindow
= false;
142 // Must set to zero, otherwise the wx routines won't size the window
143 // the second time you call the file dialog, because it thinks it is
144 // already at the requested size.. (when centering)
149 void wxFileDialog::GetPaths(wxArrayString
& paths
) const
154 if ( m_dir
.Last() != _T('\\') )
157 size_t count
= m_fileNames
.GetCount();
158 for ( size_t n
= 0; n
< count
; n
++ )
160 if (wxFileName(m_fileNames
[n
]).IsAbsolute())
161 paths
.Add(m_fileNames
[n
]);
163 paths
.Add(dir
+ m_fileNames
[n
]);
167 void wxFileDialog::GetFilenames(wxArrayString
& files
) const
172 void wxFileDialog::SetPath(const wxString
& path
)
175 wxSplitPath(path
, &m_dir
, &m_fileName
, &ext
);
177 m_fileName
<< _T('.') << ext
;
180 void wxFileDialog::DoGetPosition(int *x
, int *y
) const
183 *x
= gs_rectDialog
.x
;
185 *y
= gs_rectDialog
.y
;
189 void wxFileDialog::DoGetSize(int *width
, int *height
) const
192 *width
= gs_rectDialog
.width
;
194 *height
= gs_rectDialog
.height
;
197 void wxFileDialog::DoMoveWindow(int x
, int y
, int WXUNUSED(w
), int WXUNUSED(h
))
199 m_bMovedWindow
= true;
204 // size of the dialog can't be changed because the controls are not laid
205 // out correctly then
208 // helper used below in ShowModal(): style is used to determine whether to show
209 // the "Save file" dialog (if it contains wxFD_SAVE bit) or "Open file" one;
210 // returns true on success or false on failure in which case err is filled with
211 // the CDERR_XXX constant
212 static bool DoShowCommFileDialog(OPENFILENAME
*of
, long style
, DWORD
*err
)
214 if ( style
& wxFD_SAVE
? GetSaveFileName(of
) : GetOpenFileName(of
) )
220 // according to MSDN, CommDlgExtendedError() should work under CE as
221 // well but apparently in practice it doesn't (anybody has more
223 *err
= GetLastError();
225 *err
= CommDlgExtendedError();
232 // We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
233 // know if the OPENFILENAME declared in the currently used headers is a V5 or
234 // V4 (smaller) one so we try to manually extend the struct in case it is the
237 // We don't do this on Windows CE nor under Win64, however, as there are no
238 // compilers with old headers for these architectures
239 #if defined(__WXWINCE__) || defined(__WIN64__)
240 typedef OPENFILENAME wxOPENFILENAME
;
242 static const DWORD gs_ofStructSize
= sizeof(OPENFILENAME
);
243 #else // !__WXWINCE__ || __WIN64__
244 #define wxTRY_SMALLER_OPENFILENAME
246 struct wxOPENFILENAME
: public OPENFILENAME
248 // fields added in Windows 2000/XP comdlg32.dll version
254 // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
255 // because sizeof(OPENFILENAME) in the headers we use when compiling the
256 // library could be less if _WIN32_WINNT is not >= 0x500
257 static const DWORD wxOPENFILENAME_V5_SIZE
= 88;
259 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
260 static const DWORD wxOPENFILENAME_V4_SIZE
= 76;
262 // always try the new one first
263 static DWORD gs_ofStructSize
= wxOPENFILENAME_V5_SIZE
;
264 #endif // __WXWINCE__ || __WIN64__/!...
266 int wxFileDialog::ShowModal()
269 if (m_parent
) hWnd
= (HWND
) m_parent
->GetHWND();
270 if (!hWnd
&& wxTheApp
->GetTopWindow())
271 hWnd
= (HWND
) wxTheApp
->GetTopWindow()->GetHWND();
273 static wxChar fileNameBuffer
[ wxMAXPATH
]; // the file-name
274 wxChar titleBuffer
[ wxMAXFILE
+1+wxMAXEXT
]; // the file-name, without path
276 *fileNameBuffer
= wxT('\0');
277 *titleBuffer
= wxT('\0');
279 #if WXWIN_COMPATIBILITY_2_4
281 if ( HasFdFlag(wxHIDE_READONLY
) || HasFdFlag(wxFD_SAVE
) )
282 msw_flags
|= OFN_HIDEREADONLY
;
284 long msw_flags
= OFN_HIDEREADONLY
;
287 if ( HasFdFlag(wxFD_FILE_MUST_EXIST
) )
288 msw_flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
290 If the window has been moved the programmer is probably
291 trying to center or position it. Thus we set the callback
292 or hook function so that we can actually adjust the position.
293 Without moving or centering the dlg, it will just stay
294 in the upper left of the frame, it does not center
297 if (m_bMovedWindow
) // we need these flags.
299 msw_flags
|= OFN_EXPLORER
|OFN_ENABLEHOOK
;
301 msw_flags
|= OFN_ENABLESIZING
;
305 if ( HasFdFlag(wxFD_MULTIPLE
) )
307 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
308 msw_flags
|= OFN_EXPLORER
| OFN_ALLOWMULTISELECT
;
311 // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the
312 // standard dialog does by default (notice that under NT it does it anyhow,
313 // OFN_NOCHANGEDIR or not, see below)
314 if ( !HasFdFlag(wxFD_CHANGE_DIR
) )
316 msw_flags
|= OFN_NOCHANGEDIR
;
319 if ( HasFdFlag(wxFD_OVERWRITE_PROMPT
) )
321 msw_flags
|= OFN_OVERWRITEPROMPT
;
327 of
.lStructSize
= gs_ofStructSize
;
329 of
.lpstrTitle
= WXSTRINGCAST m_message
;
330 of
.lpstrFileTitle
= titleBuffer
;
331 of
.nMaxFileTitle
= wxMAXFILE
+ 1 + wxMAXEXT
;
333 // Convert forward slashes to backslashes (file selector doesn't like
334 // forward slashes) and also squeeze multiple consecutive slashes into one
335 // as it doesn't like two backslashes in a row neither
338 size_t i
, len
= m_dir
.length();
340 for ( i
= 0; i
< len
; i
++ )
342 wxChar ch
= m_dir
[i
];
346 // convert to backslash
352 while ( i
< len
- 1 )
354 wxChar chNext
= m_dir
[i
+ 1];
355 if ( chNext
!= _T('\\') && chNext
!= _T('/') )
358 // ignore the next one, unless it is at the start of a UNC path
372 of
.lpstrInitialDir
= dir
.c_str();
374 of
.Flags
= msw_flags
;
375 of
.lpfnHook
= wxFileDialogHookFunction
;
377 wxArrayString wildDescriptions
, wildFilters
;
379 size_t items
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
);
381 wxASSERT_MSG( items
> 0 , _T("empty wildcard list") );
383 wxString filterBuffer
;
385 for (i
= 0; i
< items
; i
++)
387 filterBuffer
+= wildDescriptions
[i
];
388 filterBuffer
+= wxT("|");
389 filterBuffer
+= wildFilters
[i
];
390 filterBuffer
+= wxT("|");
394 for (i
= 0; i
< filterBuffer
.length(); i
++ ) {
395 if ( filterBuffer
.GetChar(i
) == wxT('|') ) {
396 filterBuffer
[i
] = wxT('\0');
400 of
.lpstrFilter
= (LPTSTR
)filterBuffer
.c_str();
401 of
.nFilterIndex
= m_filterIndex
+ 1;
403 //=== Setting defaultFileName >>=========================================
405 wxStrncpy( fileNameBuffer
, (const wxChar
*)m_fileName
, wxMAXPATH
-1 );
406 fileNameBuffer
[ wxMAXPATH
-1 ] = wxT('\0');
408 of
.lpstrFile
= fileNameBuffer
; // holds returned filename
409 of
.nMaxFile
= wxMAXPATH
;
411 // we must set the default extension because otherwise Windows would check
412 // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the
413 // user types "foo" and the default extension is ".bar" we should force it
414 // to check for "foo.bar" existence and not "foo")
415 wxString defextBuffer
; // we need it to be alive until GetSaveFileName()!
416 if (HasFdFlag(wxFD_SAVE
))
418 const wxChar
* extension
= filterBuffer
;
419 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
421 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
422 extension
= extension
+ wxStrlen( extension
) + 1;
424 // use dummy name a to avoid assert in AppendExtension
425 defextBuffer
= AppendExtension(wxT("a"), extension
);
426 if (defextBuffer
.StartsWith(wxT("a.")))
428 defextBuffer
= defextBuffer
.Mid(2); // remove "a."
429 of
.lpstrDefExt
= defextBuffer
.c_str();
433 // store off before the standard windows dialog can possibly change it
434 const wxString cwdOrig
= wxGetCwd();
436 //== Execute FileDialog >>=================================================
439 bool success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
441 #ifdef wxTRY_SMALLER_OPENFILENAME
442 // the system might be too old to support the new version file dialog
443 // boxes, try with the old size
444 if ( !success
&& errCode
== CDERR_STRUCTSIZE
&&
445 of
.lStructSize
!= wxOPENFILENAME_V4_SIZE
)
447 of
.lStructSize
= wxOPENFILENAME_V4_SIZE
;
449 success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
451 if ( success
|| !errCode
)
453 // use this struct size for subsequent dialogs
454 gs_ofStructSize
= of
.lStructSize
;
457 #endif // wxTRY_SMALLER_OPENFILENAME
461 // GetOpenFileName will always change the current working directory on
462 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
463 // OFN_NOCHANGEDIR has no effect. If the user did not specify
464 // wxFD_CHANGE_DIR let's restore the current working directory to what it
465 // was before the dialog was shown.
466 if ( msw_flags
& OFN_NOCHANGEDIR
)
468 wxSetWorkingDirectory(cwdOrig
);
473 if ( ( HasFdFlag(wxFD_MULTIPLE
) ) &&
474 #if defined(OFN_EXPLORER)
475 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') )
477 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') )
478 #endif // OFN_EXPLORER
481 #if defined(OFN_EXPLORER)
482 m_dir
= fileNameBuffer
;
484 m_fileName
= &fileNameBuffer
[i
];
485 m_fileNames
.Add(m_fileName
);
486 i
+= m_fileName
.length() + 1;
488 while (fileNameBuffer
[i
] != wxT('\0'))
490 m_fileNames
.Add(&fileNameBuffer
[i
]);
491 i
+= wxStrlen(&fileNameBuffer
[i
]) + 1;
494 wxStringTokenizer
toke(fileNameBuffer
, _T(" \t\r\n"));
495 m_dir
= toke
.GetNextToken();
496 m_fileName
= toke
.GetNextToken();
497 m_fileNames
.Add(m_fileName
);
499 while (toke
.HasMoreTokens())
500 m_fileNames
.Add(toke
.GetNextToken());
501 #endif // OFN_EXPLORER
504 if ( m_dir
.Last() != _T('\\') )
507 m_path
= dir
+ m_fileName
;
508 m_filterIndex
= (int)of
.nFilterIndex
- 1;
512 //=== Adding the correct extension >>=================================
514 m_filterIndex
= (int)of
.nFilterIndex
- 1;
516 if ( !of
.nFileExtension
||
517 (of
.nFileExtension
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) )
519 // User has typed a filename without an extension:
520 const wxChar
* extension
= filterBuffer
;
521 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
523 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
524 extension
= extension
+ wxStrlen( extension
) + 1;
526 m_fileName
= AppendExtension(fileNameBuffer
, extension
);
527 wxStrncpy(fileNameBuffer
, m_fileName
.c_str(), wxMin(m_fileName
.length(), wxMAXPATH
-1));
528 fileNameBuffer
[wxMin(m_fileName
.length(), wxMAXPATH
-1)] = wxT('\0');
531 m_path
= fileNameBuffer
;
532 m_fileName
= wxFileNameFromPath(fileNameBuffer
);
533 m_fileNames
.Add(m_fileName
);
534 m_dir
= wxPathOnly(fileNameBuffer
);
540 // common dialog failed - why?
543 // this msg is only for developers so don't translate it
544 wxLogError(wxT("Common dialog failed with error code %0lx."),
547 //else: it was just cancelled
549 #endif // __WXDEBUG__
551 return success
? wxID_OK
: wxID_CANCEL
;
555 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)