Applied patch [ 1195797 ] Fixes to compile on Win64
[wxWidgets.git] / src / msw / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "filedlg.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_FILEDLG && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
32
33 #ifndef WX_PRECOMP
34 #include "wx/utils.h"
35 #include "wx/msgdlg.h"
36 #include "wx/filedlg.h"
37 #include "wx/filefn.h"
38 #include "wx/intl.h"
39 #include "wx/log.h"
40 #include "wx/app.h"
41 #endif
42
43 #include "wx/msw/wrapcdlg.h"
44
45 #include <stdlib.h>
46 #include <string.h>
47
48 #include "wx/filename.h"
49 #include "wx/tokenzr.h"
50 #include "wx/math.h"
51
52 #include "wx/msw/missing.h"
53
54 // ----------------------------------------------------------------------------
55 // constants
56 // ----------------------------------------------------------------------------
57
58 #ifdef __WIN32__
59 # define wxMAXPATH 65534
60 #else
61 # define wxMAXPATH 1024
62 #endif
63
64 # define wxMAXFILE 1024
65
66 # define wxMAXEXT 5
67
68 // ----------------------------------------------------------------------------
69 // globals
70 // ----------------------------------------------------------------------------
71
72 // standard dialog size
73 static wxRect gs_rectDialog(0, 0, 428, 266);
74
75 // ============================================================================
76 // implementation
77 // ============================================================================
78
79 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
80
81 // ----------------------------------------------------------------------------
82 // hook function for moving the dialog
83 // ----------------------------------------------------------------------------
84
85 UINT_PTR APIENTRY
86 wxFileDialogHookFunction(HWND hDlg,
87 UINT iMsg,
88 WPARAM WXUNUSED(wParam),
89 LPARAM lParam)
90 {
91 HWND hwndDialog;
92 hwndDialog = ::GetParent( hDlg );
93 switch (iMsg)
94 {
95 case WM_DESTROY:
96 {
97 RECT dlgRect;
98 GetWindowRect( hwndDialog, & dlgRect );
99 gs_rectDialog.x = dlgRect.left;
100 gs_rectDialog.y = dlgRect.top;
101 gs_rectDialog.width = dlgRect.right - dlgRect.left;
102 gs_rectDialog.height = dlgRect.bottom - dlgRect.top;
103 }
104 break;
105
106 case WM_NOTIFY:
107 {
108 OFNOTIFY * pNotifyCode;
109 pNotifyCode = (LPOFNOTIFY) lParam;
110 if (CDN_INITDONE == (pNotifyCode->hdr).code)
111 {
112 SetWindowPos( hwndDialog, HWND_TOP,
113 gs_rectDialog.x,
114 gs_rectDialog.y,
115 gs_rectDialog.width,
116 gs_rectDialog.height,
117 SWP_NOZORDER|SWP_NOSIZE);
118 }
119 }
120 break;
121 }
122
123 // do the default processing
124 return 0;
125 }
126
127 // ----------------------------------------------------------------------------
128 // wxFileDialog
129 // ----------------------------------------------------------------------------
130
131 wxFileDialog::wxFileDialog(wxWindow *parent,
132 const wxString& message,
133 const wxString& defaultDir,
134 const wxString& defaultFileName,
135 const wxString& wildCard,
136 long style,
137 const wxPoint& pos)
138 : wxFileDialogBase(parent, message, defaultDir, defaultFileName,
139 wildCard, style, pos)
140
141 {
142 if ( ( m_dialogStyle & wxMULTIPLE ) && ( m_dialogStyle & wxSAVE ) )
143 m_dialogStyle &= ~wxMULTIPLE;
144
145 m_bMovedWindow = false;
146
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)
150 gs_rectDialog.x =
151 gs_rectDialog.y = 0;
152
153 }
154 void wxFileDialog::GetPaths(wxArrayString& paths) const
155 {
156 paths.Empty();
157
158 wxString dir(m_dir);
159 if ( m_dir.Last() != _T('\\') )
160 dir += _T('\\');
161
162 size_t count = m_fileNames.GetCount();
163 for ( size_t n = 0; n < count; n++ )
164 {
165 if (wxFileName(m_fileNames[n]).IsAbsolute())
166 paths.Add(m_fileNames[n]);
167 else
168 paths.Add(dir + m_fileNames[n]);
169 }
170 }
171
172 void wxFileDialog::GetFilenames(wxArrayString& files) const
173 {
174 files = m_fileNames;
175 }
176
177 void wxFileDialog::SetPath(const wxString& path)
178 {
179 wxString ext;
180 wxSplitPath(path, &m_dir, &m_fileName, &ext);
181 if ( !ext.empty() )
182 m_fileName << _T('.') << ext;
183 }
184
185 void wxFileDialog::DoGetPosition( int *x, int *y ) const
186 {
187 *x = gs_rectDialog.x;
188 *y = gs_rectDialog.y;
189 }
190
191
192 void wxFileDialog::DoGetSize(int *width, int *height) const
193 {
194 *width = gs_rectDialog.width;
195 *height = gs_rectDialog.height;
196 }
197
198 void wxFileDialog::DoMoveWindow(int x, int y, int WXUNUSED(width), int WXUNUSED(height))
199 {
200 m_bMovedWindow = true;
201
202 gs_rectDialog.x = x;
203 gs_rectDialog.y = y;
204
205 /*
206 The width and height can not be set by the programmer
207 its just not possible. But the program can get the
208 size of the Dlg after it has been shown, in case they need
209 that data.
210 */
211 }
212
213 int wxFileDialog::ShowModal()
214 {
215 HWND hWnd = 0;
216 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
217 if (!hWnd && wxTheApp->GetTopWindow())
218 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
219
220 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
221 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
222
223 *fileNameBuffer = wxT('\0');
224 *titleBuffer = wxT('\0');
225
226 #if WXWIN_COMPATIBILITY_2_4
227 long msw_flags = 0;
228 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
229 msw_flags |= OFN_HIDEREADONLY;
230 #else
231 long msw_flags = OFN_HIDEREADONLY;
232 #endif
233
234 if ( m_dialogStyle & wxFILE_MUST_EXIST )
235 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
236 /*
237 If the window has been moved the programmer is probably
238 trying to center or position it. Thus we set the callback
239 or hook function so that we can actually adjust the position.
240 Without moving or centering the dlg, it will just stay
241 in the upper left of the frame, it does not center
242 automatically.. One additional note, when the hook is
243 enabled, the PLACES BAR in the dlg (shown on later versions
244 of windows (2000 and XP) will automatically be turned off
245 according to the MSDN docs. This is normal. If the
246 programmer needs the PLACES BAR (left side of dlg) they
247 just shouldn't move or center the dlg.
248 */
249 if (m_bMovedWindow) // we need these flags.
250 {
251 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK;
252 #ifndef __WXWINCE__
253 msw_flags |= OFN_ENABLESIZING;
254 #endif
255 }
256
257 if (m_dialogStyle & wxMULTIPLE )
258 {
259 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
260 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
261 }
262
263 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
264 // standard dialog does by default
265 if ( !(m_dialogStyle & wxCHANGE_DIR) )
266 {
267 msw_flags |= OFN_NOCHANGEDIR;
268 }
269
270 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
271 {
272 msw_flags |= OFN_OVERWRITEPROMPT;
273 }
274
275 OPENFILENAME of;
276 wxZeroMemory(of);
277
278 // the OPENFILENAME struct has been extended in newer version of
279 // comcdlg32.dll, but as we don't use the extended fields anyhow, set
280 // the struct size to the old value - otherwise, the programs compiled
281 // with new headers will not work with the old libraries
282 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
283 of.lStructSize = sizeof(OPENFILENAME) -
284 (sizeof(void *) + 2*sizeof(DWORD));
285 #else // old headers
286 of.lStructSize = sizeof(OPENFILENAME);
287 #endif
288
289 of.hwndOwner = hWnd;
290 of.lpstrTitle = WXSTRINGCAST m_message;
291 of.lpstrFileTitle = titleBuffer;
292 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
293
294 // Convert forward slashes to backslashes (file selector doesn't like
295 // forward slashes) and also squeeze multiple consecutive slashes into one
296 // as it doesn't like two backslashes in a row neither
297
298 wxString dir;
299 size_t i, len = m_dir.length();
300 dir.reserve(len);
301 for ( i = 0; i < len; i++ )
302 {
303 wxChar ch = m_dir[i];
304 switch ( ch )
305 {
306 case _T('/'):
307 // convert to backslash
308 ch = _T('\\');
309
310 // fall through
311
312 case _T('\\'):
313 while ( i < len - 1 )
314 {
315 wxChar chNext = m_dir[i + 1];
316 if ( chNext != _T('\\') && chNext != _T('/') )
317 break;
318
319 // ignore the next one, unless it is at the start of a UNC path
320 if (i > 0)
321 i++;
322 else
323 break;
324 }
325 // fall through
326
327 default:
328 // normal char
329 dir += ch;
330 }
331 }
332
333 of.lpstrInitialDir = dir.c_str();
334
335 of.Flags = msw_flags;
336 of.lpfnHook = wxFileDialogHookFunction;
337
338 wxArrayString wildDescriptions, wildFilters;
339
340 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
341
342 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
343
344 wxString filterBuffer;
345
346 for (i = 0; i < items ; i++)
347 {
348 filterBuffer += wildDescriptions[i];
349 filterBuffer += wxT("|");
350 filterBuffer += wildFilters[i];
351 filterBuffer += wxT("|");
352 }
353
354 // Replace | with \0
355 for (i = 0; i < filterBuffer.Len(); i++ ) {
356 if ( filterBuffer.GetChar(i) == wxT('|') ) {
357 filterBuffer[i] = wxT('\0');
358 }
359 }
360
361 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
362 of.nFilterIndex = m_filterIndex + 1;
363
364 //=== Setting defaultFileName >>=========================================
365
366 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
367 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
368
369 of.lpstrFile = fileNameBuffer; // holds returned filename
370 of.nMaxFile = wxMAXPATH;
371
372 // we must set the default extension because otherwise Windows would check
373 // for the existing of a wrong file with wxOVERWRITE_PROMPT (i.e. if the
374 // user types "foo" and the default extension is ".bar" we should force it
375 // to check for "foo.bar" existence and not "foo")
376 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
377 if (m_dialogStyle & wxSAVE)
378 {
379 const wxChar* extension = filterBuffer;
380 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
381
382 for( int i = 0; i < maxFilter; i++ ) // get extension
383 extension = extension + wxStrlen( extension ) + 1;
384
385 // use dummy name a to avoid assert in AppendExtension
386 defextBuffer = AppendExtension(wxT("a"), extension);
387 if (defextBuffer.StartsWith(wxT("a.")))
388 {
389 defextBuffer.Mid(2);
390 of.lpstrDefExt = defextBuffer.c_str();
391 }
392 }
393
394 //== Execute FileDialog >>=================================================
395
396 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
397 : GetOpenFileName(&of)) != 0;
398
399 DWORD errCode = CommDlgExtendedError();
400
401 #ifdef __WIN32__
402 if (!success && (errCode == CDERR_STRUCTSIZE))
403 {
404 // The struct size has changed so try a smaller or bigger size
405
406 int oldStructSize = of.lStructSize;
407 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
408 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
409 : (GetOpenFileName(&of) != 0);
410 errCode = CommDlgExtendedError();
411
412 if (!success && (errCode == CDERR_STRUCTSIZE))
413 {
414 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
415 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
416 : (GetOpenFileName(&of) != 0);
417 }
418 }
419 #endif // __WIN32__
420
421 if ( success )
422 {
423 m_fileNames.Empty();
424
425 if ( ( m_dialogStyle & wxMULTIPLE ) &&
426 #if defined(OFN_EXPLORER)
427 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
428 #else
429 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
430 #endif // OFN_EXPLORER
431 )
432 {
433 #if defined(OFN_EXPLORER)
434 m_dir = fileNameBuffer;
435 i = of.nFileOffset;
436 m_fileName = &fileNameBuffer[i];
437 m_fileNames.Add(m_fileName);
438 i += m_fileName.Len() + 1;
439
440 while (fileNameBuffer[i] != wxT('\0'))
441 {
442 m_fileNames.Add(&fileNameBuffer[i]);
443 i += wxStrlen(&fileNameBuffer[i]) + 1;
444 }
445 #else
446 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
447 m_dir = toke.GetNextToken();
448 m_fileName = toke.GetNextToken();
449 m_fileNames.Add(m_fileName);
450
451 while (toke.HasMoreTokens())
452 m_fileNames.Add(toke.GetNextToken());
453 #endif // OFN_EXPLORER
454
455 wxString dir(m_dir);
456 if ( m_dir.Last() != _T('\\') )
457 dir += _T('\\');
458
459 m_path = dir + m_fileName;
460 m_filterIndex = (int)of.nFilterIndex - 1;
461 }
462 else
463 {
464 //=== Adding the correct extension >>=================================
465
466 m_filterIndex = (int)of.nFilterIndex - 1;
467
468 if ( !of.nFileExtension ||
469 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
470 {
471 // User has typed a filename without an extension:
472 const wxChar* extension = filterBuffer;
473 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
474
475 for( int i = 0; i < maxFilter; i++ ) // get extension
476 extension = extension + wxStrlen( extension ) + 1;
477
478 m_fileName = AppendExtension(fileNameBuffer, extension);
479 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
480 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
481 }
482
483 m_path = fileNameBuffer;
484 m_fileName = wxFileNameFromPath(fileNameBuffer);
485 m_fileNames.Add(m_fileName);
486 m_dir = wxPathOnly(fileNameBuffer);
487 }
488 }
489 else
490 {
491 // common dialog failed - why?
492 #ifdef __WXDEBUG__
493 DWORD dwErr = CommDlgExtendedError();
494 if ( dwErr != 0 )
495 {
496 // this msg is only for developers
497 wxLogError(wxT("Common dialog failed with error code %0lx."),
498 dwErr);
499 }
500 //else: it was just cancelled
501 #endif
502 }
503
504 return success ? wxID_OK : wxID_CANCEL;
505
506 }
507
508 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)
509