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 for the old Windows systems where the dialog wasn't 
  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
), 
  92                 OPENFILENAME
* ofn 
= reinterpret_cast<OPENFILENAME 
*>(lParam
); 
  93                 reinterpret_cast<wxFileDialog 
*>(ofn
->lCustData
) 
  94                     ->MSWOnInitDialogHook((WXHWND
)hDlg
); 
 101                 OFNOTIFY 
*pNotifyCode 
= reinterpret_cast<OFNOTIFY 
*>(lParam
); 
 102                 if ( pNotifyCode
->hdr
.code 
== CDN_INITDONE 
) 
 104                     reinterpret_cast<wxFileDialog 
*>( 
 105                                         pNotifyCode
->lpOFN
->lCustData
) 
 106                         ->MSWOnInitDone((WXHWND
)hDlg
); 
 112             // reuse the position used for the dialog the next time by default 
 114             // NB: at least under Windows 2003 this is useless as after the 
 115             //     first time it's shown the dialog always remembers its size 
 116             //     and position itself and ignores any later SetWindowPos calls 
 117             wxCopyRECTToRect(wxGetWindowRect(::GetParent(hDlg
)), gs_rectDialog
); 
 121     // do the default processing 
 125 // ---------------------------------------------------------------------------- 
 127 // ---------------------------------------------------------------------------- 
 129 wxFileDialog::wxFileDialog(wxWindow 
*parent
, 
 130                            const wxString
& message
, 
 131                            const wxString
& defaultDir
, 
 132                            const wxString
& defaultFileName
, 
 133                            const wxString
& wildCard
, 
 137                            const wxString
& name
) 
 138             : wxFileDialogBase(parent
, message
, defaultDir
, defaultFileName
, 
 139                                wildCard
, style
, pos
, sz
, name
) 
 142     // NB: all style checks are done by wxFileDialogBase::Create 
 144     m_bMovedWindow 
= false; 
 147     // Must set to zero, otherwise the wx routines won't size the window 
 148     // the second time you call the file dialog, because it thinks it is 
 149     // already at the requested size.. (when centering) 
 154 void wxFileDialog::GetPaths(wxArrayString
& paths
) const 
 159     if ( m_dir
.Last() != _T('\\') ) 
 162     size_t count 
= m_fileNames
.GetCount(); 
 163     for ( size_t n 
= 0; n 
< count
; n
++ ) 
 165         if (wxFileName(m_fileNames
[n
]).IsAbsolute()) 
 166             paths
.Add(m_fileNames
[n
]); 
 168             paths
.Add(dir 
+ m_fileNames
[n
]); 
 172 void wxFileDialog::GetFilenames(wxArrayString
& files
) const 
 177 void wxFileDialog::SetPath(const wxString
& path
) 
 180     wxFileName::SplitPath(path
, &m_dir
, &m_fileName
, &ext
); 
 182         m_fileName 
<< _T('.') << ext
; 
 185 void wxFileDialog::DoGetPosition(int *x
, int *y
) const 
 188         *x 
= gs_rectDialog
.x
; 
 190         *y 
= gs_rectDialog
.y
; 
 193 void wxFileDialog::DoGetSize(int *width
, int *height
) const 
 196         *width 
= gs_rectDialog
.width
; 
 198         *height 
= gs_rectDialog
.height
; 
 201 void wxFileDialog::DoMoveWindow(int x
, int y
, int WXUNUSED(w
), int WXUNUSED(h
)) 
 206     // our HWND is only set when we're called from MSWOnInitDone(), test if 
 208     HWND hwnd 
= GetHwnd(); 
 211         // size of the dialog can't be changed because the controls are not 
 212         // laid out correctly then 
 213        ::SetWindowPos(hwnd
, HWND_TOP
, x
, y
, 0, 0, SWP_NOZORDER 
| SWP_NOSIZE
); 
 215     else // just remember that we were requested to move the window 
 217         m_bMovedWindow 
= true; 
 219         // if Centre() had been called before, it shouldn't be taken into 
 225 void wxFileDialog::DoCentre(int dir
) 
 228     m_bMovedWindow 
= true; 
 230     // it's unnecessary to do anything else at this stage as we'll redo it in 
 231     // MSWOnInitDone() anyhow 
 234 void wxFileDialog::MSWOnInitDone(WXHWND hDlg
) 
 236     // note the the dialog is the parent window: hDlg is a child of it when 
 237     // OFN_EXPLORER is used 
 238     HWND hFileDlg 
= ::GetParent((HWND
)hDlg
); 
 240     // set HWND so that our DoMoveWindow() works correctly 
 241     SetHWND((WXHWND
)hFileDlg
); 
 245         // now we have the real dialog size, remember it 
 247         GetWindowRect(hFileDlg
, &rect
); 
 248         gs_rectDialog 
= wxRectFromRECT(rect
); 
 250         // and position the window correctly: notice that we must use the base 
 251         // class version as our own doesn't do anything except setting flags 
 252         wxFileDialogBase::DoCentre(m_centreDir
); 
 254     else // need to just move it to the correct place 
 256         SetPosition(gs_rectDialog
.GetPosition()); 
 259     // we shouldn't destroy this HWND 
 263 // helper used below in ShowCommFileDialog(): style is used to determine 
 264 // whether to show the "Save file" dialog (if it contains wxFD_SAVE bit) or 
 265 // "Open file" one; returns true on success or false on failure in which case 
 266 // err is filled with the CDERR_XXX constant 
 267 static bool DoShowCommFileDialog(OPENFILENAME 
*of
, long style
, DWORD 
*err
) 
 269     if ( style 
& wxFD_SAVE 
? GetSaveFileName(of
) : GetOpenFileName(of
) ) 
 275         // according to MSDN, CommDlgExtendedError() should work under CE as 
 276         // well but apparently in practice it doesn't (anybody has more 
 278         *err 
= GetLastError(); 
 280         *err 
= CommDlgExtendedError(); 
 287 // We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't 
 288 // know if the OPENFILENAME declared in the currently used headers is a V5 or 
 289 // V4 (smaller) one so we try to manually extend the struct in case it is the 
 292 // We don't do this on Windows CE nor under Win64, however, as there are no 
 293 // compilers with old headers for these architectures 
 294 #if defined(__WXWINCE__) || defined(__WIN64__) 
 295     typedef OPENFILENAME wxOPENFILENAME
; 
 297     static const DWORD gs_ofStructSize 
= sizeof(OPENFILENAME
); 
 298 #else // !__WXWINCE__ || __WIN64__ 
 299     #define wxTRY_SMALLER_OPENFILENAME 
 301     struct wxOPENFILENAME 
: public OPENFILENAME
 
 303         // fields added in Windows 2000/XP comdlg32.dll version 
 309     // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it 
 310     // because sizeof(OPENFILENAME) in the headers we use when compiling the 
 311     // library could be less if _WIN32_WINNT is not >= 0x500 
 312     static const DWORD wxOPENFILENAME_V5_SIZE 
= 88; 
 314     // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK 
 315     static const DWORD wxOPENFILENAME_V4_SIZE 
= 76; 
 317     // always try the new one first 
 318     static DWORD gs_ofStructSize 
= wxOPENFILENAME_V5_SIZE
; 
 319 #endif // __WXWINCE__ || __WIN64__/!... 
 321 static bool ShowCommFileDialog(OPENFILENAME 
*of
, long style
) 
 324     bool success 
= DoShowCommFileDialog(of
, style
, &errCode
); 
 326 #ifdef wxTRY_SMALLER_OPENFILENAME 
 327     // the system might be too old to support the new version file dialog 
 328     // boxes, try with the old size 
 329     if ( !success 
&& errCode 
== CDERR_STRUCTSIZE 
&& 
 330             of
->lStructSize 
!= wxOPENFILENAME_V4_SIZE 
) 
 332         of
->lStructSize 
= wxOPENFILENAME_V4_SIZE
; 
 334         success 
= DoShowCommFileDialog(of
, style
, &errCode
); 
 336         if ( success 
|| !errCode 
) 
 338             // use this struct size for subsequent dialogs 
 339             gs_ofStructSize 
= of
->lStructSize
; 
 342 #endif // wxTRY_SMALLER_OPENFILENAME 
 345             // FNERR_INVALIDFILENAME is not defined under CE (besides we don't 
 346             // use CommDlgExtendedError() there anyhow) 
 348             errCode 
== FNERR_INVALIDFILENAME 
&& 
 349 #endif // !__WXWINCE__ 
 352         // this can happen if the default file name is invalid, try without it 
 354         of
->lpstrFile
[0] = _T('\0'); 
 355         success 
= DoShowCommFileDialog(of
, style
, &errCode
); 
 360         // common dialog failed - why? 
 363             wxLogError(_("File dialog failed with error code %0lx."), errCode
); 
 365         //else: it was just cancelled 
 374 void wxFileDialog::MSWOnInitDialogHook(WXHWND hwnd
) 
 378    CreateExtraControl(); 
 382 #endif // __WXWINCE__ 
 384 int wxFileDialog::ShowModal() 
 387     if (m_parent
) hWnd 
= (HWND
) m_parent
->GetHWND(); 
 388     if (!hWnd 
&& wxTheApp
->GetTopWindow()) 
 389         hWnd 
= (HWND
) wxTheApp
->GetTopWindow()->GetHWND(); 
 391     static wxChar fileNameBuffer 
[ wxMAXPATH 
];           // the file-name 
 392     wxChar        titleBuffer    
[ wxMAXFILE
+1+wxMAXEXT 
];  // the file-name, without path 
 394     *fileNameBuffer 
= wxT('\0'); 
 395     *titleBuffer    
= wxT('\0'); 
 397     long msw_flags 
= OFN_HIDEREADONLY
; 
 399     if ( HasFdFlag(wxFD_FILE_MUST_EXIST
) ) 
 400         msw_flags 
|= OFN_PATHMUSTEXIST 
| OFN_FILEMUSTEXIST
; 
 402         If the window has been moved the programmer is probably 
 403         trying to center or position it.  Thus we set the callback 
 404         or hook function so that we can actually adjust the position. 
 405         Without moving or centering the dlg, it will just stay 
 406         in the upper left of the frame, it does not center 
 409     if (m_bMovedWindow 
|| HasExtraControlCreator()) // we need these flags. 
 411         msw_flags 
|= OFN_EXPLORER
|OFN_ENABLEHOOK
; 
 413         msw_flags 
|= OFN_ENABLESIZING
; 
 417     if ( HasFdFlag(wxFD_MULTIPLE
) ) 
 419         // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT 
 420         msw_flags 
|= OFN_EXPLORER 
| OFN_ALLOWMULTISELECT
; 
 423     // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the 
 424     // standard dialog does by default (notice that under NT it does it anyhow, 
 425     // OFN_NOCHANGEDIR or not, see below) 
 426     if ( !HasFdFlag(wxFD_CHANGE_DIR
) ) 
 428         msw_flags 
|= OFN_NOCHANGEDIR
; 
 431     if ( HasFdFlag(wxFD_OVERWRITE_PROMPT
) ) 
 433         msw_flags 
|= OFN_OVERWRITEPROMPT
; 
 439     of
.lStructSize       
= gs_ofStructSize
; 
 441     of
.lpstrTitle        
= m_message
.wx_str(); 
 442     of
.lpstrFileTitle    
= titleBuffer
; 
 443     of
.nMaxFileTitle     
= wxMAXFILE 
+ 1 + wxMAXEXT
; 
 447     if ( HasExtraControlCreator() ) 
 449         msw_flags 
|= OFN_ENABLETEMPLATEHANDLE
; 
 451         hgbl
.Init(256, GMEM_ZEROINIT
); 
 452         GlobalPtrLock 
hgblLock(hgbl
); 
 453         LPDLGTEMPLATE lpdt 
= static_cast<LPDLGTEMPLATE
>(hgblLock
.Get()); 
 455         // Define a dialog box. 
 457         lpdt
->style 
= DS_CONTROL 
| WS_CHILD 
| WS_CLIPSIBLINGS
; 
 458         lpdt
->cdit 
= 0;         // Number of controls 
 462         // convert the size of the extra controls to the dialog units 
 463         const wxSize extraSize 
= GetExtraControlSize(); 
 464         const LONG baseUnits 
= ::GetDialogBaseUnits(); 
 465         lpdt
->cx 
= ::MulDiv(extraSize
.x
, 4, LOWORD(baseUnits
)); 
 466         lpdt
->cy 
= ::MulDiv(extraSize
.y
, 8, HIWORD(baseUnits
)); 
 468         // after the DLGTEMPLATE there are 3 additional WORDs for dialog menu, 
 469         // class and title, all three set to zeros. 
 471         of
.hInstance 
= (HINSTANCE
)lpdt
; 
 473 #endif // __WXWINCE__ 
 475     // Convert forward slashes to backslashes (file selector doesn't like 
 476     // forward slashes) and also squeeze multiple consecutive slashes into one 
 477     // as it doesn't like two backslashes in a row neither 
 480     size_t    i
, len 
= m_dir
.length(); 
 482     for ( i 
= 0; i 
< len
; i
++ ) 
 484         wxChar ch 
= m_dir
[i
]; 
 488                 // convert to backslash 
 494                 while ( i 
< len 
- 1 ) 
 496                     wxChar chNext 
= m_dir
[i 
+ 1]; 
 497                     if ( chNext 
!= _T('\\') && chNext 
!= _T('/') ) 
 500                     // ignore the next one, unless it is at the start of a UNC path 
 514     of
.lpstrInitialDir   
= dir
.c_str(); 
 516     of
.Flags             
= msw_flags
; 
 517     of
.lpfnHook          
= wxFileDialogHookFunction
; 
 518     of
.lCustData         
= (LPARAM
)this; 
 520     wxArrayString wildDescriptions
, wildFilters
; 
 522     size_t items 
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
); 
 524     wxASSERT_MSG( items 
> 0 , _T("empty wildcard list") ); 
 526     wxString filterBuffer
; 
 528     for (i 
= 0; i 
< items 
; i
++) 
 530         filterBuffer 
+= wildDescriptions
[i
]; 
 531         filterBuffer 
+= wxT("|"); 
 532         filterBuffer 
+= wildFilters
[i
]; 
 533         filterBuffer 
+= wxT("|"); 
 537     for (i 
= 0; i 
< filterBuffer
.length(); i
++ ) { 
 538         if ( filterBuffer
.GetChar(i
) == wxT('|') ) { 
 539             filterBuffer
[i
] = wxT('\0'); 
 543     of
.lpstrFilter  
= (LPTSTR
)filterBuffer
.wx_str(); 
 544     of
.nFilterIndex 
= m_filterIndex 
+ 1; 
 546     //=== Setting defaultFileName >>========================================= 
 548     wxStrlcpy(fileNameBuffer
, m_fileName
.c_str(), WXSIZEOF(fileNameBuffer
)); 
 550     of
.lpstrFile 
= fileNameBuffer
;  // holds returned filename 
 551     of
.nMaxFile  
= wxMAXPATH
; 
 553     // we must set the default extension because otherwise Windows would check 
 554     // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the 
 555     // user types "foo" and the default extension is ".bar" we should force it 
 556     // to check for "foo.bar" existence and not "foo") 
 557     wxString defextBuffer
; // we need it to be alive until GetSaveFileName()! 
 558     if (HasFdFlag(wxFD_SAVE
)) 
 560         const wxChar
* extension 
= filterBuffer
.wx_str(); 
 561         int maxFilter 
= (int)(of
.nFilterIndex
*2L) - 1; 
 563         for( int i 
= 0; i 
< maxFilter
; i
++ )           // get extension 
 564             extension 
= extension 
+ wxStrlen( extension 
) + 1; 
 566         // use dummy name a to avoid assert in AppendExtension 
 567         defextBuffer 
= AppendExtension(wxT("a"), extension
); 
 568         if (defextBuffer
.StartsWith(wxT("a."))) 
 570             defextBuffer 
= defextBuffer
.Mid(2); // remove "a." 
 571             of
.lpstrDefExt 
= defextBuffer
.c_str(); 
 575     // store off before the standard windows dialog can possibly change it 
 576     const wxString cwdOrig 
= wxGetCwd(); 
 578     //== Execute FileDialog >>================================================= 
 580     if ( !ShowCommFileDialog(&of
, m_windowStyle
) ) 
 583     // GetOpenFileName will always change the current working directory on 
 584     // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag 
 585     // OFN_NOCHANGEDIR has no effect.  If the user did not specify 
 586     // wxFD_CHANGE_DIR let's restore the current working directory to what it 
 587     // was before the dialog was shown. 
 588     if ( msw_flags 
& OFN_NOCHANGEDIR 
) 
 590         wxSetWorkingDirectory(cwdOrig
); 
 595     if ( ( HasFdFlag(wxFD_MULTIPLE
) ) && 
 596 #if defined(OFN_EXPLORER) 
 597          ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') ) 
 599          ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') ) 
 600 #endif // OFN_EXPLORER 
 603 #if defined(OFN_EXPLORER) 
 604         m_dir 
= fileNameBuffer
; 
 606         m_fileName 
= &fileNameBuffer
[i
]; 
 607         m_fileNames
.Add(m_fileName
); 
 608         i 
+= m_fileName
.length() + 1; 
 610         while (fileNameBuffer
[i
] != wxT('\0')) 
 612             m_fileNames
.Add(&fileNameBuffer
[i
]); 
 613             i 
+= wxStrlen(&fileNameBuffer
[i
]) + 1; 
 616         wxStringTokenizer 
toke(fileNameBuffer
, _T(" \t\r\n")); 
 617         m_dir 
= toke
.GetNextToken(); 
 618         m_fileName 
= toke
.GetNextToken(); 
 619         m_fileNames
.Add(m_fileName
); 
 621         while (toke
.HasMoreTokens()) 
 622             m_fileNames
.Add(toke
.GetNextToken()); 
 623 #endif // OFN_EXPLORER 
 626         if ( m_dir
.Last() != _T('\\') ) 
 629         m_path 
= dir 
+ m_fileName
; 
 630         m_filterIndex 
= (int)of
.nFilterIndex 
- 1; 
 634         //=== Adding the correct extension >>================================= 
 636         m_filterIndex 
= (int)of
.nFilterIndex 
- 1; 
 638         if ( !of
.nFileExtension 
|| 
 639              (of
.nFileExtension 
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) ) 
 641             // User has typed a filename without an extension: 
 642             const wxChar
* extension 
= filterBuffer
.wx_str(); 
 643             int   maxFilter 
= (int)(of
.nFilterIndex
*2L) - 1; 
 645             for( int i 
= 0; i 
< maxFilter
; i
++ )           // get extension 
 646                 extension 
= extension 
+ wxStrlen( extension 
) + 1; 
 648             m_fileName 
= AppendExtension(fileNameBuffer
, extension
); 
 649             wxStrlcpy(fileNameBuffer
, m_fileName
.c_str(), WXSIZEOF(fileNameBuffer
)); 
 652         m_path 
= fileNameBuffer
; 
 653         m_fileName 
= wxFileNameFromPath(fileNameBuffer
); 
 654         m_fileNames
.Add(m_fileName
); 
 655         m_dir 
= wxPathOnly(fileNameBuffer
); 
 662 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)