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 long msw_flags
= OFN_HIDEREADONLY
;
281 if ( HasFdFlag(wxFD_FILE_MUST_EXIST
) )
282 msw_flags
|= OFN_PATHMUSTEXIST
| OFN_FILEMUSTEXIST
;
284 If the window has been moved the programmer is probably
285 trying to center or position it. Thus we set the callback
286 or hook function so that we can actually adjust the position.
287 Without moving or centering the dlg, it will just stay
288 in the upper left of the frame, it does not center
291 if (m_bMovedWindow
) // we need these flags.
293 msw_flags
|= OFN_EXPLORER
|OFN_ENABLEHOOK
;
295 msw_flags
|= OFN_ENABLESIZING
;
299 if ( HasFdFlag(wxFD_MULTIPLE
) )
301 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
302 msw_flags
|= OFN_EXPLORER
| OFN_ALLOWMULTISELECT
;
305 // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the
306 // standard dialog does by default (notice that under NT it does it anyhow,
307 // OFN_NOCHANGEDIR or not, see below)
308 if ( !HasFdFlag(wxFD_CHANGE_DIR
) )
310 msw_flags
|= OFN_NOCHANGEDIR
;
313 if ( HasFdFlag(wxFD_OVERWRITE_PROMPT
) )
315 msw_flags
|= OFN_OVERWRITEPROMPT
;
321 of
.lStructSize
= gs_ofStructSize
;
323 of
.lpstrTitle
= WXSTRINGCAST m_message
;
324 of
.lpstrFileTitle
= titleBuffer
;
325 of
.nMaxFileTitle
= wxMAXFILE
+ 1 + wxMAXEXT
;
327 // Convert forward slashes to backslashes (file selector doesn't like
328 // forward slashes) and also squeeze multiple consecutive slashes into one
329 // as it doesn't like two backslashes in a row neither
332 size_t i
, len
= m_dir
.length();
334 for ( i
= 0; i
< len
; i
++ )
336 wxChar ch
= m_dir
[i
];
340 // convert to backslash
346 while ( i
< len
- 1 )
348 wxChar chNext
= m_dir
[i
+ 1];
349 if ( chNext
!= _T('\\') && chNext
!= _T('/') )
352 // ignore the next one, unless it is at the start of a UNC path
366 of
.lpstrInitialDir
= dir
.c_str();
368 of
.Flags
= msw_flags
;
369 of
.lpfnHook
= wxFileDialogHookFunction
;
371 wxArrayString wildDescriptions
, wildFilters
;
373 size_t items
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
);
375 wxASSERT_MSG( items
> 0 , _T("empty wildcard list") );
377 wxString filterBuffer
;
379 for (i
= 0; i
< items
; i
++)
381 filterBuffer
+= wildDescriptions
[i
];
382 filterBuffer
+= wxT("|");
383 filterBuffer
+= wildFilters
[i
];
384 filterBuffer
+= wxT("|");
388 for (i
= 0; i
< filterBuffer
.length(); i
++ ) {
389 if ( filterBuffer
.GetChar(i
) == wxT('|') ) {
390 filterBuffer
[i
] = wxT('\0');
394 of
.lpstrFilter
= (LPTSTR
)filterBuffer
.c_str();
395 of
.nFilterIndex
= m_filterIndex
+ 1;
397 //=== Setting defaultFileName >>=========================================
399 wxStrncpy( fileNameBuffer
, (const wxChar
*)m_fileName
, wxMAXPATH
-1 );
400 fileNameBuffer
[ wxMAXPATH
-1 ] = wxT('\0');
402 of
.lpstrFile
= fileNameBuffer
; // holds returned filename
403 of
.nMaxFile
= wxMAXPATH
;
405 // we must set the default extension because otherwise Windows would check
406 // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the
407 // user types "foo" and the default extension is ".bar" we should force it
408 // to check for "foo.bar" existence and not "foo")
409 wxString defextBuffer
; // we need it to be alive until GetSaveFileName()!
410 if (HasFdFlag(wxFD_SAVE
))
412 const wxChar
* extension
= filterBuffer
;
413 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
415 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
416 extension
= extension
+ wxStrlen( extension
) + 1;
418 // use dummy name a to avoid assert in AppendExtension
419 defextBuffer
= AppendExtension(wxT("a"), extension
);
420 if (defextBuffer
.StartsWith(wxT("a.")))
422 defextBuffer
= defextBuffer
.Mid(2); // remove "a."
423 of
.lpstrDefExt
= defextBuffer
.c_str();
427 // store off before the standard windows dialog can possibly change it
428 const wxString cwdOrig
= wxGetCwd();
430 //== Execute FileDialog >>=================================================
433 bool success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
435 #ifdef wxTRY_SMALLER_OPENFILENAME
436 // the system might be too old to support the new version file dialog
437 // boxes, try with the old size
438 if ( !success
&& errCode
== CDERR_STRUCTSIZE
&&
439 of
.lStructSize
!= wxOPENFILENAME_V4_SIZE
)
441 of
.lStructSize
= wxOPENFILENAME_V4_SIZE
;
443 success
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
);
445 if ( success
|| !errCode
)
447 // use this struct size for subsequent dialogs
448 gs_ofStructSize
= of
.lStructSize
;
451 #endif // wxTRY_SMALLER_OPENFILENAME
455 // GetOpenFileName will always change the current working directory on
456 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
457 // OFN_NOCHANGEDIR has no effect. If the user did not specify
458 // wxFD_CHANGE_DIR let's restore the current working directory to what it
459 // was before the dialog was shown.
460 if ( msw_flags
& OFN_NOCHANGEDIR
)
462 wxSetWorkingDirectory(cwdOrig
);
467 if ( ( HasFdFlag(wxFD_MULTIPLE
) ) &&
468 #if defined(OFN_EXPLORER)
469 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') )
471 ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') )
472 #endif // OFN_EXPLORER
475 #if defined(OFN_EXPLORER)
476 m_dir
= fileNameBuffer
;
478 m_fileName
= &fileNameBuffer
[i
];
479 m_fileNames
.Add(m_fileName
);
480 i
+= m_fileName
.length() + 1;
482 while (fileNameBuffer
[i
] != wxT('\0'))
484 m_fileNames
.Add(&fileNameBuffer
[i
]);
485 i
+= wxStrlen(&fileNameBuffer
[i
]) + 1;
488 wxStringTokenizer
toke(fileNameBuffer
, _T(" \t\r\n"));
489 m_dir
= toke
.GetNextToken();
490 m_fileName
= toke
.GetNextToken();
491 m_fileNames
.Add(m_fileName
);
493 while (toke
.HasMoreTokens())
494 m_fileNames
.Add(toke
.GetNextToken());
495 #endif // OFN_EXPLORER
498 if ( m_dir
.Last() != _T('\\') )
501 m_path
= dir
+ m_fileName
;
502 m_filterIndex
= (int)of
.nFilterIndex
- 1;
506 //=== Adding the correct extension >>=================================
508 m_filterIndex
= (int)of
.nFilterIndex
- 1;
510 if ( !of
.nFileExtension
||
511 (of
.nFileExtension
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) )
513 // User has typed a filename without an extension:
514 const wxChar
* extension
= filterBuffer
;
515 int maxFilter
= (int)(of
.nFilterIndex
*2L) - 1;
517 for( int i
= 0; i
< maxFilter
; i
++ ) // get extension
518 extension
= extension
+ wxStrlen( extension
) + 1;
520 m_fileName
= AppendExtension(fileNameBuffer
, extension
);
521 wxStrncpy(fileNameBuffer
, m_fileName
.c_str(), wxMin(m_fileName
.length(), wxMAXPATH
-1));
522 fileNameBuffer
[wxMin(m_fileName
.length(), wxMAXPATH
-1)] = wxT('\0');
525 m_path
= fileNameBuffer
;
526 m_fileName
= wxFileNameFromPath(fileNameBuffer
);
527 m_fileNames
.Add(m_fileName
);
528 m_dir
= wxPathOnly(fileNameBuffer
);
534 // common dialog failed - why?
537 // this msg is only for developers so don't translate it
538 wxLogError(wxT("Common dialog failed with error code %0lx."),
541 //else: it was just cancelled
543 #endif // __WXDEBUG__
545 return success
? wxID_OK
: wxID_CANCEL
;
549 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)