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 // we have no way to retrieve the dialog size before it is shown so calling 
  72 // Centre() before ShowModal() doesn't work correctly (and we can't do it 
  73 // after), hence we set a special flag and recenter the dialog when it's about 
  75 static bool gs_centerDialog 
= false; 
  77 // ============================================================================ 
  79 // ============================================================================ 
  81 IMPLEMENT_CLASS(wxFileDialog
, wxFileDialogBase
) 
  83 // ---------------------------------------------------------------------------- 
  84 // hook function for moving the dialog 
  85 // ---------------------------------------------------------------------------- 
  88 wxFileDialogHookFunction(HWND      hDlg
, 
  90                          WPARAM    
WXUNUSED(wParam
), 
  97                 OFNOTIFY 
*pNotifyCode 
= wx_reinterpret_cast(OFNOTIFY 
*, lParam
); 
  98                 if ( pNotifyCode
->hdr
.code 
== CDN_INITDONE 
) 
 100                     wx_reinterpret_cast(wxFileDialog 
*, 
 101                                         pNotifyCode
->lpOFN
->lCustData
) 
 102                         ->MSWOnInitDone((WXHWND
)hDlg
); 
 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; 
 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 
 184         *x 
= gs_rectDialog
.x
; 
 186         *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
)) 
 202     // our HWND is only set when we're called from MSWOnInitDone(), test if 
 204     HWND hwnd 
= GetHwnd(); 
 207         // size of the dialog can't be changed because the controls are not 
 208         // laid out correctly then 
 209        ::SetWindowPos(hwnd
, HWND_TOP
, x
, y
, 0, 0, SWP_NOZORDER 
| SWP_NOSIZE
); 
 211     else // just remember that we were requested to move the window 
 213         m_bMovedWindow 
= true; 
 215         // if Centre() had been called before, it shouldn't be taken into 
 221 void wxFileDialog::DoCentre(int dir
) 
 224     m_bMovedWindow 
= true; 
 226     // it's unnecessary to do anything else at this stage as we'll redo it in 
 227     // MSWOnInitDone() anyhow 
 230 void wxFileDialog::MSWOnInitDone(WXHWND hDlg
) 
 232     // note the the dialog is the parent window: hDlg is a child of it when 
 233     // OFN_EXPLORER is used 
 234     HWND hFileDlg 
= ::GetParent((HWND
)hDlg
); 
 236     // set HWND so that our DoMoveWindow() works correctly 
 237     SetHWND((WXHWND
)hFileDlg
); 
 241         // now we have the real dialog size, remember it 
 243         GetWindowRect(hFileDlg
, &rect
); 
 244         gs_rectDialog 
= wxRectFromRECT(rect
); 
 246         // and position the window correctly: notice that we must use the base 
 247         // class version as our own doesn't do anything except setting flags 
 248         wxFileDialogBase::DoCentre(m_centreDir
); 
 250     else // need to just move it to the correct place 
 252         SetPosition(gs_rectDialog
.GetPosition()); 
 255     // we shouldn't destroy this HWND 
 259 // helper used below in ShowModal(): style is used to determine whether to show 
 260 // the "Save file" dialog (if it contains wxFD_SAVE bit) or "Open file" one; 
 261 // returns true on success or false on failure in which case err is filled with 
 262 // the CDERR_XXX constant 
 263 static bool DoShowCommFileDialog(OPENFILENAME 
*of
, long style
, DWORD 
*err
) 
 265     if ( style 
& wxFD_SAVE 
? GetSaveFileName(of
) : GetOpenFileName(of
) ) 
 271         // according to MSDN, CommDlgExtendedError() should work under CE as 
 272         // well but apparently in practice it doesn't (anybody has more 
 274         *err 
= GetLastError(); 
 276         *err 
= CommDlgExtendedError(); 
 283 // We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't 
 284 // know if the OPENFILENAME declared in the currently used headers is a V5 or 
 285 // V4 (smaller) one so we try to manually extend the struct in case it is the 
 288 // We don't do this on Windows CE nor under Win64, however, as there are no 
 289 // compilers with old headers for these architectures 
 290 #if defined(__WXWINCE__) || defined(__WIN64__) 
 291     typedef OPENFILENAME wxOPENFILENAME
; 
 293     static const DWORD gs_ofStructSize 
= sizeof(OPENFILENAME
); 
 294 #else // !__WXWINCE__ || __WIN64__ 
 295     #define wxTRY_SMALLER_OPENFILENAME 
 297     struct wxOPENFILENAME 
: public OPENFILENAME
 
 299         // fields added in Windows 2000/XP comdlg32.dll version 
 305     // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it 
 306     // because sizeof(OPENFILENAME) in the headers we use when compiling the 
 307     // library could be less if _WIN32_WINNT is not >= 0x500 
 308     static const DWORD wxOPENFILENAME_V5_SIZE 
= 88; 
 310     // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK 
 311     static const DWORD wxOPENFILENAME_V4_SIZE 
= 76; 
 313     // always try the new one first 
 314     static DWORD gs_ofStructSize 
= wxOPENFILENAME_V5_SIZE
; 
 315 #endif // __WXWINCE__ || __WIN64__/!... 
 317 int wxFileDialog::ShowModal() 
 320     if (m_parent
) hWnd 
= (HWND
) m_parent
->GetHWND(); 
 321     if (!hWnd 
&& wxTheApp
->GetTopWindow()) 
 322         hWnd 
= (HWND
) wxTheApp
->GetTopWindow()->GetHWND(); 
 324     static wxChar fileNameBuffer 
[ wxMAXPATH 
];           // the file-name 
 325     wxChar        titleBuffer    
[ wxMAXFILE
+1+wxMAXEXT 
];  // the file-name, without path 
 327     *fileNameBuffer 
= wxT('\0'); 
 328     *titleBuffer    
= wxT('\0'); 
 330     long msw_flags 
= OFN_HIDEREADONLY
; 
 332     if ( HasFdFlag(wxFD_FILE_MUST_EXIST
) ) 
 333         msw_flags 
|= OFN_PATHMUSTEXIST 
| OFN_FILEMUSTEXIST
; 
 335         If the window has been moved the programmer is probably 
 336         trying to center or position it.  Thus we set the callback 
 337         or hook function so that we can actually adjust the position. 
 338         Without moving or centering the dlg, it will just stay 
 339         in the upper left of the frame, it does not center 
 342     if (m_bMovedWindow
) // we need these flags. 
 344         msw_flags 
|= OFN_EXPLORER
|OFN_ENABLEHOOK
; 
 346         msw_flags 
|= OFN_ENABLESIZING
; 
 350     if ( HasFdFlag(wxFD_MULTIPLE
) ) 
 352         // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT 
 353         msw_flags 
|= OFN_EXPLORER 
| OFN_ALLOWMULTISELECT
; 
 356     // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the 
 357     // standard dialog does by default (notice that under NT it does it anyhow, 
 358     // OFN_NOCHANGEDIR or not, see below) 
 359     if ( !HasFdFlag(wxFD_CHANGE_DIR
) ) 
 361         msw_flags 
|= OFN_NOCHANGEDIR
; 
 364     if ( HasFdFlag(wxFD_OVERWRITE_PROMPT
) ) 
 366         msw_flags 
|= OFN_OVERWRITEPROMPT
; 
 372     of
.lStructSize       
= gs_ofStructSize
; 
 374     of
.lpstrTitle        
= m_message
.wx_str(); 
 375     of
.lpstrFileTitle    
= titleBuffer
; 
 376     of
.nMaxFileTitle     
= wxMAXFILE 
+ 1 + wxMAXEXT
; 
 378     // Convert forward slashes to backslashes (file selector doesn't like 
 379     // forward slashes) and also squeeze multiple consecutive slashes into one 
 380     // as it doesn't like two backslashes in a row neither 
 383     size_t    i
, len 
= m_dir
.length(); 
 385     for ( i 
= 0; i 
< len
; i
++ ) 
 387         wxChar ch 
= m_dir
[i
]; 
 391                 // convert to backslash 
 397                 while ( i 
< len 
- 1 ) 
 399                     wxChar chNext 
= m_dir
[i 
+ 1]; 
 400                     if ( chNext 
!= _T('\\') && chNext 
!= _T('/') ) 
 403                     // ignore the next one, unless it is at the start of a UNC path 
 417     of
.lpstrInitialDir   
= dir
.c_str(); 
 419     of
.Flags             
= msw_flags
; 
 420     of
.lpfnHook          
= wxFileDialogHookFunction
; 
 421     of
.lCustData         
= (LPARAM
)this; 
 423     wxArrayString wildDescriptions
, wildFilters
; 
 425     size_t items 
= wxParseCommonDialogsFilter(m_wildCard
, wildDescriptions
, wildFilters
); 
 427     wxASSERT_MSG( items 
> 0 , _T("empty wildcard list") ); 
 429     wxString filterBuffer
; 
 431     for (i 
= 0; i 
< items 
; i
++) 
 433         filterBuffer 
+= wildDescriptions
[i
]; 
 434         filterBuffer 
+= wxT("|"); 
 435         filterBuffer 
+= wildFilters
[i
]; 
 436         filterBuffer 
+= wxT("|"); 
 440     for (i 
= 0; i 
< filterBuffer
.length(); i
++ ) { 
 441         if ( filterBuffer
.GetChar(i
) == wxT('|') ) { 
 442             filterBuffer
[i
] = wxT('\0'); 
 446     of
.lpstrFilter  
= (LPTSTR
)filterBuffer
.wx_str(); 
 447     of
.nFilterIndex 
= m_filterIndex 
+ 1; 
 449     //=== Setting defaultFileName >>========================================= 
 451     wxStrncpy(fileNameBuffer
, m_fileName
, wxMAXPATH
-1); 
 452     fileNameBuffer
[ wxMAXPATH
-1 ] = wxT('\0'); 
 454     of
.lpstrFile 
= fileNameBuffer
;  // holds returned filename 
 455     of
.nMaxFile  
= wxMAXPATH
; 
 457     // we must set the default extension because otherwise Windows would check 
 458     // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the 
 459     // user types "foo" and the default extension is ".bar" we should force it 
 460     // to check for "foo.bar" existence and not "foo") 
 461     wxString defextBuffer
; // we need it to be alive until GetSaveFileName()! 
 462     if (HasFdFlag(wxFD_SAVE
)) 
 464         const wxChar
* extension 
= filterBuffer
.wx_str(); 
 465         int maxFilter 
= (int)(of
.nFilterIndex
*2L) - 1; 
 467         for( int i 
= 0; i 
< maxFilter
; i
++ )           // get extension 
 468             extension 
= extension 
+ wxStrlen( extension 
) + 1; 
 470         // use dummy name a to avoid assert in AppendExtension 
 471         defextBuffer 
= AppendExtension(wxT("a"), extension
); 
 472         if (defextBuffer
.StartsWith(wxT("a."))) 
 474             defextBuffer 
= defextBuffer
.Mid(2); // remove "a." 
 475             of
.lpstrDefExt 
= defextBuffer
.c_str(); 
 479     // store off before the standard windows dialog can possibly change it 
 480     const wxString cwdOrig 
= wxGetCwd(); 
 482     //== Execute FileDialog >>================================================= 
 485     bool success 
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
); 
 487 #ifdef wxTRY_SMALLER_OPENFILENAME 
 488     // the system might be too old to support the new version file dialog 
 489     // boxes, try with the old size 
 490     if ( !success 
&& errCode 
== CDERR_STRUCTSIZE 
&& 
 491             of
.lStructSize 
!= wxOPENFILENAME_V4_SIZE 
) 
 493         of
.lStructSize 
= wxOPENFILENAME_V4_SIZE
; 
 495         success 
= DoShowCommFileDialog(&of
, m_windowStyle
, &errCode
); 
 497         if ( success 
|| !errCode 
) 
 499             // use this struct size for subsequent dialogs 
 500             gs_ofStructSize 
= of
.lStructSize
; 
 503 #endif // wxTRY_SMALLER_OPENFILENAME 
 507         // GetOpenFileName will always change the current working directory on 
 508         // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag 
 509         // OFN_NOCHANGEDIR has no effect.  If the user did not specify 
 510         // wxFD_CHANGE_DIR let's restore the current working directory to what it 
 511         // was before the dialog was shown. 
 512         if ( msw_flags 
& OFN_NOCHANGEDIR 
) 
 514             wxSetWorkingDirectory(cwdOrig
); 
 519         if ( ( HasFdFlag(wxFD_MULTIPLE
) ) && 
 520 #if defined(OFN_EXPLORER) 
 521              ( fileNameBuffer
[of
.nFileOffset
-1] == wxT('\0') ) 
 523              ( fileNameBuffer
[of
.nFileOffset
-1] == wxT(' ') ) 
 524 #endif // OFN_EXPLORER 
 527 #if defined(OFN_EXPLORER) 
 528             m_dir 
= fileNameBuffer
; 
 530             m_fileName 
= &fileNameBuffer
[i
]; 
 531             m_fileNames
.Add(m_fileName
); 
 532             i 
+= m_fileName
.length() + 1; 
 534             while (fileNameBuffer
[i
] != wxT('\0')) 
 536                 m_fileNames
.Add(&fileNameBuffer
[i
]); 
 537                 i 
+= wxStrlen(&fileNameBuffer
[i
]) + 1; 
 540             wxStringTokenizer 
toke(fileNameBuffer
, _T(" \t\r\n")); 
 541             m_dir 
= toke
.GetNextToken(); 
 542             m_fileName 
= toke
.GetNextToken(); 
 543             m_fileNames
.Add(m_fileName
); 
 545             while (toke
.HasMoreTokens()) 
 546                 m_fileNames
.Add(toke
.GetNextToken()); 
 547 #endif // OFN_EXPLORER 
 550             if ( m_dir
.Last() != _T('\\') ) 
 553             m_path 
= dir 
+ m_fileName
; 
 554             m_filterIndex 
= (int)of
.nFilterIndex 
- 1; 
 558             //=== Adding the correct extension >>================================= 
 560             m_filterIndex 
= (int)of
.nFilterIndex 
- 1; 
 562             if ( !of
.nFileExtension 
|| 
 563                  (of
.nFileExtension 
&& fileNameBuffer
[of
.nFileExtension
] == wxT('\0')) ) 
 565                 // User has typed a filename without an extension: 
 566                 const wxChar
* extension 
= filterBuffer
.wx_str(); 
 567                 int   maxFilter 
= (int)(of
.nFilterIndex
*2L) - 1; 
 569                 for( int i 
= 0; i 
< maxFilter
; i
++ )           // get extension 
 570                     extension 
= extension 
+ wxStrlen( extension 
) + 1; 
 572                 m_fileName 
= AppendExtension(fileNameBuffer
, extension
); 
 573                 wxStrncpy(fileNameBuffer
, m_fileName
.c_str(), wxMin(m_fileName
.length(), wxMAXPATH
-1)); 
 574                 fileNameBuffer
[wxMin(m_fileName
.length(), wxMAXPATH
-1)] = wxT('\0'); 
 577             m_path 
= fileNameBuffer
; 
 578             m_fileName 
= wxFileNameFromPath(fileNameBuffer
); 
 579             m_fileNames
.Add(m_fileName
); 
 580             m_dir 
= wxPathOnly(fileNameBuffer
); 
 586         // common dialog failed - why? 
 589             // this msg is only for developers so don't translate it 
 590             wxLogError(wxT("Common dialog failed with error code %0lx."), 
 593         //else: it was just cancelled 
 595 #endif // __WXDEBUG__ 
 597     return success 
? wxID_OK 
: wxID_CANCEL
; 
 601 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)