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
),
88 hwndDialog
= ::GetParent( hDlg
);
94 GetWindowRect( hwndDialog
, & dlgRect
);
95 gs_rectDialog
.x
= dlgRect
.left
;
96 gs_rectDialog
.y
= dlgRect
.top
;
97 gs_rectDialog
.width
= dlgRect
.right
- dlgRect
.left
;
98 gs_rectDialog
.height
= dlgRect
.bottom
- dlgRect
.top
;
104 OFNOTIFY
* pNotifyCode
;
105 pNotifyCode
= (LPOFNOTIFY
) lParam
;
106 if (CDN_INITDONE
== (pNotifyCode
->hdr
).code
)
108 SetWindowPos( hwndDialog
, HWND_TOP
,
112 gs_rectDialog
.height
,
113 SWP_NOZORDER
|SWP_NOSIZE
);
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
,
134 : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
,
135 wildCard
, style
, pos
)
138 if ( ( m_dialogStyle
& wxMULTIPLE
) && ( m_dialogStyle
& wxSAVE
) )
139 m_dialogStyle
&= ~wxMULTIPLE
;
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
183 *x
= gs_rectDialog
.x
;
184 *y
= gs_rectDialog
.y
;
188 void wxFileDialog
::DoGetSize(int *width
, int *height
) const
190 *width
= gs_rectDialog
.width
;
191 *height
= gs_rectDialog
.height
;
194 void wxFileDialog
::DoMoveWindow(int x
, int y
, int WXUNUSED(width
), int WXUNUSED(height
))
196 m_bMovedWindow
= true;
202 The width and height can not be set by the programmer
203 its just not possible. But the program can get the
204 size of the Dlg after it has been shown, in case they need
209 // helper used below in ShowModal(): style is used to determine whether to show
210 // the "Save file" dialog (if it contains wxSAVE 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
& wxSAVE ?
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 // Basically, the problem is that we need to use the "new" size of the
234 // OPENFILENAME structure
235 // (OPENFILENAME_SIZE_VERSION_400 + void* + DWORD + DWORD)
236 // in Windows 2000 and XP so that the new-style file dialog with the
237 // "Places Bar" shows up. Unfortunately, there seems to be no reliable way
238 // to test for it in the headers, so we need to always make one
239 // with the extra bytes.
241 // We don't do this on Windows CE, however.
243 typedef OPENFILENAME wxOPENFILENAME
;
245 static const DWORD wxOPENFILENAME_V5_SIZE
= sizeof(OPENFILENAME
);
246 #else // !__WXWINCE__
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;
262 #endif // __WXWINCE__/!__WXWINCE__
264 // always try the new one first
265 static DWORD gs_ofStructSize
= wxOPENFILENAME_V5_SIZE
;
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_dialogStyle
& wxHIDE_READONLY
) || (m_dialogStyle
& wxSAVE
) )
283 msw_flags
|= OFN_HIDEREADONLY
;
285 long msw_flags
= OFN_HIDEREADONLY
;
288 if ( m_dialogStyle
& wxFILE_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_dialogStyle
& wxMULTIPLE
)
308 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
309 msw_flags
|= OFN_EXPLORER
| OFN_ALLOWMULTISELECT
;
312 // if wxCHANGE_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_dialogStyle
& wxCHANGE_DIR
) )
317 msw_flags
|= OFN_NOCHANGEDIR
;
320 if ( m_dialogStyle
& wxOVERWRITE_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
.Len(); 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 wxOVERWRITE_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_dialogStyle
& wxSAVE
)
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_dialogStyle
, &errCode
);
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_dialogStyle
, &errCode
);
452 if ( success
|| !errCode
)
454 // use this struct size for subsequent dialogs
455 gs_ofStructSize
= of
.lStructSize
;
458 #endif // !__WXWINCE__
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 // wxCHANGE_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_dialogStyle
& wxMULTIPLE
) &&
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
.Len() + 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
.Len(), wxMAXPATH
-1));
529 fileNameBuffer
[wxMin(m_fileName
.Len(), 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__)