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     if ( ( m_windowStyle 
& wxFD_MULTIPLE 
) && ( m_windowStyle 
& wxFD_SAVE 
) ) 
 140         m_windowStyle 
&= ~wxFD_MULTIPLE
; 
 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__)