]> git.saurik.com Git - wxWidgets.git/blame - src/msw/filedlg.cpp
don't crash when destroying a not initialized socket (patch 1489095)
[wxWidgets.git] / src / msw / filedlg.cpp
CommitLineData
2bda0e17 1/////////////////////////////////////////////////////////////////////////////
f6bcfd97 2// Name: src/msw/filedlg.cpp
2bda0e17
KB
3// Purpose: wxFileDialog
4// Author: Julian Smart
5// Modified by:
6// Created: 01/02/97
7// RCS-ID: $Id$
6c9a19aa 8// Copyright: (c) Julian Smart
65571936 9// Licence: wxWindows licence
2bda0e17
KB
10/////////////////////////////////////////////////////////////////////////////
11
f6bcfd97
BP
12// ============================================================================
13// declarations
14// ============================================================================
15
16// ----------------------------------------------------------------------------
17// headers
18// ----------------------------------------------------------------------------
19
2bda0e17
KB
20// For compilers that support precompilation, includes "wx.h".
21#include "wx/wxprec.h"
22
23#ifdef __BORLANDC__
ba681060 24 #pragma hdrstop
2bda0e17
KB
25#endif
26
3180bc0e 27#if wxUSE_FILEDLG && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
1e6feb95 28
2bda0e17 29#ifndef WX_PRECOMP
ba681060
VZ
30 #include "wx/utils.h"
31 #include "wx/msgdlg.h"
ba681060 32 #include "wx/filedlg.h"
2b5f62a0 33 #include "wx/filefn.h"
ba681060 34 #include "wx/intl.h"
2662e49e 35 #include "wx/log.h"
f6bcfd97 36 #include "wx/app.h"
8f177c8e 37#endif
2bda0e17 38
660296aa 39#include "wx/msw/wrapcdlg.h"
2bda0e17 40
2bda0e17
KB
41#include <stdlib.h>
42#include <string.h>
43
8ad9ca97 44#include "wx/filename.h"
8f177c8e 45#include "wx/tokenzr.h"
b713f891 46#include "wx/math.h"
8f177c8e 47
41b8fe99 48#include "wx/msw/missing.h"
6e8aa701 49
f6bcfd97
BP
50// ----------------------------------------------------------------------------
51// constants
52// ----------------------------------------------------------------------------
53
54#ifdef __WIN32__
2b5f62a0 55# define wxMAXPATH 65534
f6bcfd97
BP
56#else
57# define wxMAXPATH 1024
58#endif
59
60# define wxMAXFILE 1024
61
62# define wxMAXEXT 5
63
0b11099d
VZ
64// ----------------------------------------------------------------------------
65// globals
66// ----------------------------------------------------------------------------
67
68// standard dialog size
69static wxRect gs_rectDialog(0, 0, 428, 266);
70
f6bcfd97
BP
71// ============================================================================
72// implementation
73// ============================================================================
74
f74172ab 75IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
2bda0e17 76
0b11099d
VZ
77// ----------------------------------------------------------------------------
78// hook function for moving the dialog
79// ----------------------------------------------------------------------------
80
106d80ad 81UINT_PTR APIENTRY
0b11099d
VZ
82wxFileDialogHookFunction(HWND hDlg,
83 UINT iMsg,
84 WPARAM WXUNUSED(wParam),
85 LPARAM lParam)
86{
cb80db46 87 switch ( iMsg )
0b11099d 88 {
0b11099d
VZ
89 case WM_NOTIFY:
90 {
cb80db46
VZ
91 OFNOTIFY *pNotifyCode = wx_reinterpret_cast(OFNOTIFY *, lParam);
92 if ( pNotifyCode->hdr.code == CDN_INITDONE )
0b11099d 93 {
cb80db46
VZ
94 // note that we need to move the parent window: hDlg is a
95 // child of it when OFN_EXPLORER is used
96 ::SetWindowPos
97 (
98 ::GetParent(hDlg),
99 HWND_TOP,
100 gs_rectDialog.x, gs_rectDialog.y,
101 0, 0,
102 SWP_NOZORDER | SWP_NOSIZE
103 );
0b11099d
VZ
104 }
105 }
106 break;
cb80db46
VZ
107
108 case WM_DESTROY:
109 // reuse the position used for the dialog the next time by default
110 //
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);
115 break;
0b11099d
VZ
116 }
117
118 // do the default processing
119 return 0;
120}
121
f6bcfd97 122// ----------------------------------------------------------------------------
b600ed13 123// wxFileDialog
f6bcfd97
BP
124// ----------------------------------------------------------------------------
125
2b5f62a0
VZ
126wxFileDialog::wxFileDialog(wxWindow *parent,
127 const wxString& message,
128 const wxString& defaultDir,
129 const wxString& defaultFileName,
130 const wxString& wildCard,
131 long style,
ff3e84ff
VZ
132 const wxPoint& pos,
133 const wxSize& sz,
134 const wxString& name)
0b11099d 135 : wxFileDialogBase(parent, message, defaultDir, defaultFileName,
ff3e84ff 136 wildCard, style, pos, sz, name)
f74172ab 137
2bda0e17 138{
ff3e84ff
VZ
139 if ( ( m_windowStyle & wxMULTIPLE ) && ( m_windowStyle & wxSAVE ) )
140 m_windowStyle &= ~wxMULTIPLE;
2bda0e17 141
0b11099d
VZ
142 m_bMovedWindow = false;
143
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)
147 gs_rectDialog.x =
148 gs_rectDialog.y = 0;
149
150}
c61f4f6d
VZ
151void wxFileDialog::GetPaths(wxArrayString& paths) const
152{
153 paths.Empty();
154
155 wxString dir(m_dir);
156 if ( m_dir.Last() != _T('\\') )
157 dir += _T('\\');
158
159 size_t count = m_fileNames.GetCount();
160 for ( size_t n = 0; n < count; n++ )
161 {
8ad9ca97
JS
162 if (wxFileName(m_fileNames[n]).IsAbsolute())
163 paths.Add(m_fileNames[n]);
164 else
165 paths.Add(dir + m_fileNames[n]);
c61f4f6d
VZ
166 }
167}
168
89654c9a
VZ
169void wxFileDialog::GetFilenames(wxArrayString& files) const
170{
171 files = m_fileNames;
172}
173
2b5f62a0
VZ
174void wxFileDialog::SetPath(const wxString& path)
175{
176 wxString ext;
177 wxSplitPath(path, &m_dir, &m_fileName, &ext);
178 if ( !ext.empty() )
179 m_fileName << _T('.') << ext;
180}
181
cb80db46 182void wxFileDialog::DoGetPosition(int *x, int *y) const
0b11099d 183{
cb80db46
VZ
184 if ( x )
185 *x = gs_rectDialog.x;
186 if ( y )
187 *y = gs_rectDialog.y;
0b11099d
VZ
188}
189
190
191void wxFileDialog::DoGetSize(int *width, int *height) const
192{
cb80db46
VZ
193 if ( width )
194 *width = gs_rectDialog.width;
195 if ( height )
196 *height = gs_rectDialog.height;
0b11099d
VZ
197}
198
cb80db46 199void wxFileDialog::DoMoveWindow(int x, int y, int WXUNUSED(w), int WXUNUSED(h))
0b11099d
VZ
200{
201 m_bMovedWindow = true;
202
203 gs_rectDialog.x = x;
204 gs_rectDialog.y = y;
205
cb80db46
VZ
206 // size of the dialog can't be changed because the controls are not laid
207 // out correctly then
0b11099d
VZ
208}
209
c46c1fb8
VZ
210// helper used below in ShowModal(): style is used to determine whether to show
211// the "Save file" dialog (if it contains wxSAVE 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
214static bool DoShowCommFileDialog(OPENFILENAME *of, long style, DWORD *err)
215{
216 if ( style & wxSAVE ? GetSaveFileName(of) : GetOpenFileName(of) )
217 return true;
218
219 if ( err )
220 {
221#ifdef __WXWINCE__
222 // according to MSDN, CommDlgExtendedError() should work under CE as
223 // well but apparently in practice it doesn't (anybody has more
224 // details?)
225 *err = GetLastError();
226#else
227 *err = CommDlgExtendedError();
228#endif
229 }
230
231 return false;
232}
233
04227efc
VZ
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
237// old one.
5bb37216 238//
04227efc
VZ
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__)
5bb37216
VZ
242 typedef OPENFILENAME wxOPENFILENAME;
243
04227efc
VZ
244 static const DWORD gs_ofStructSize = sizeof(OPENFILENAME);
245#else // !__WXWINCE__ || __WIN64__
246 #define wxTRY_SMALLER_OPENFILENAME
247
5bb37216
VZ
248 struct wxOPENFILENAME : public OPENFILENAME
249 {
250 // fields added in Windows 2000/XP comdlg32.dll version
251 void *pVoid;
252 DWORD dw1;
253 DWORD dw2;
254 };
255
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;
260
261 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
262 static const DWORD wxOPENFILENAME_V4_SIZE = 76;
5bb37216 263
04227efc
VZ
264 // always try the new one first
265 static DWORD gs_ofStructSize = wxOPENFILENAME_V5_SIZE;
266#endif // __WXWINCE__ || __WIN64__/!...
5bb37216 267
c61f4f6d 268int wxFileDialog::ShowModal()
2bda0e17 269{
1f2f0331
VZ
270 HWND hWnd = 0;
271 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
f6bcfd97
BP
272 if (!hWnd && wxTheApp->GetTopWindow())
273 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
2bda0e17 274
f6bcfd97
BP
275 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
276 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
2bda0e17 277
223d09f6
KB
278 *fileNameBuffer = wxT('\0');
279 *titleBuffer = wxT('\0');
2bda0e17 280
21416306 281#if WXWIN_COMPATIBILITY_2_4
2bda0e17 282 long msw_flags = 0;
ff3e84ff 283 if ( (m_windowStyle & wxHIDE_READONLY) || (m_windowStyle & wxSAVE) )
1f2f0331 284 msw_flags |= OFN_HIDEREADONLY;
21416306
WS
285#else
286 long msw_flags = OFN_HIDEREADONLY;
287#endif
288
ff3e84ff 289 if ( m_windowStyle & wxFILE_MUST_EXIST )
1f2f0331 290 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
0b11099d
VZ
291 /*
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
5bb37216 297 automatically.
0b11099d 298 */
503528dc
JS
299 if (m_bMovedWindow) // we need these flags.
300 {
301 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK;
302#ifndef __WXWINCE__
303 msw_flags |= OFN_ENABLESIZING;
304#endif
305 }
6e8aa701 306
ff3e84ff 307 if (m_windowStyle & wxMULTIPLE )
6e8aa701
VZ
308 {
309 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
310 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
311 }
312
99d1b93d 313 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
af1f0a76
VZ
314 // standard dialog does by default (notice that under NT it does it anyhow,
315 // OFN_NOCHANGEDIR or not, see below)
ff3e84ff 316 if ( !(m_windowStyle & wxCHANGE_DIR) )
6e8aa701
VZ
317 {
318 msw_flags |= OFN_NOCHANGEDIR;
319 }
ac95e671 320
ff3e84ff 321 if ( m_windowStyle & wxOVERWRITE_PROMPT )
99d1b93d
VZ
322 {
323 msw_flags |= OFN_OVERWRITEPROMPT;
324 }
ac95e671 325
5bb37216 326 wxOPENFILENAME of;
f6bcfd97
BP
327 wxZeroMemory(of);
328
5bb37216 329 of.lStructSize = gs_ofStructSize;
e15e548b 330 of.hwndOwner = hWnd;
837e5743 331 of.lpstrTitle = WXSTRINGCAST m_message;
e15e548b 332 of.lpstrFileTitle = titleBuffer;
5bb37216 333 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT;
2bda0e17 334
0bc9b25e 335 // Convert forward slashes to backslashes (file selector doesn't like
99d1b93d
VZ
336 // forward slashes) and also squeeze multiple consecutive slashes into one
337 // as it doesn't like two backslashes in a row neither
0627d091 338
cbe874bd
WS
339 wxString dir;
340 size_t i, len = m_dir.length();
99d1b93d 341 dir.reserve(len);
0627d091 342 for ( i = 0; i < len; i++ )
99d1b93d
VZ
343 {
344 wxChar ch = m_dir[i];
345 switch ( ch )
346 {
347 case _T('/'):
348 // convert to backslash
349 ch = _T('\\');
350
351 // fall through
0bc9b25e 352
99d1b93d
VZ
353 case _T('\\'):
354 while ( i < len - 1 )
355 {
356 wxChar chNext = m_dir[i + 1];
357 if ( chNext != _T('\\') && chNext != _T('/') )
358 break;
359
04d93c3a
CE
360 // ignore the next one, unless it is at the start of a UNC path
361 if (i > 0)
362 i++;
363 else
0b11099d 364 break;
99d1b93d
VZ
365 }
366 // fall through
367
368 default:
369 // normal char
370 dir += ch;
371 }
372 }
373
374 of.lpstrInitialDir = dir.c_str();
2bda0e17 375
e15e548b 376 of.Flags = msw_flags;
0b11099d 377 of.lpfnHook = wxFileDialogHookFunction;
2bda0e17 378
daf32463 379 wxArrayString wildDescriptions, wildFilters;
2bda0e17 380
daf32463 381 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
2bda0e17 382
daf32463 383 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
2bda0e17 384
1f2f0331 385 wxString filterBuffer;
2bda0e17 386
daf32463
WS
387 for (i = 0; i < items ; i++)
388 {
389 filterBuffer += wildDescriptions[i];
390 filterBuffer += wxT("|");
391 filterBuffer += wildFilters[i];
392 filterBuffer += wxT("|");
574c0bbf
JS
393 }
394
574c0bbf 395 // Replace | with \0
0bc9b25e 396 for (i = 0; i < filterBuffer.Len(); i++ ) {
223d09f6
KB
397 if ( filterBuffer.GetChar(i) == wxT('|') ) {
398 filterBuffer[i] = wxT('\0');
e15e548b
VZ
399 }
400 }
2bda0e17 401
daf32463 402 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
cc42eb7a 403 of.nFilterIndex = m_filterIndex + 1;
2bda0e17
KB
404
405 //=== Setting defaultFileName >>=========================================
406
f6bcfd97
BP
407 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
408 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
2bda0e17 409
e15e548b 410 of.lpstrFile = fileNameBuffer; // holds returned filename
f6bcfd97 411 of.nMaxFile = wxMAXPATH;
2bda0e17 412
90bddb85
VZ
413 // we must set the default extension because otherwise Windows would check
414 // for the existing of a wrong file with wxOVERWRITE_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()!
ff3e84ff 418 if (m_windowStyle & wxSAVE)
90bddb85
VZ
419 {
420 const wxChar* extension = filterBuffer;
421 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
422
423 for( int i = 0; i < maxFilter; i++ ) // get extension
424 extension = extension + wxStrlen( extension ) + 1;
425
426 // use dummy name a to avoid assert in AppendExtension
427 defextBuffer = AppendExtension(wxT("a"), extension);
428 if (defextBuffer.StartsWith(wxT("a.")))
429 {
430 defextBuffer.Mid(2);
431 of.lpstrDefExt = defextBuffer.c_str();
432 }
433 }
0b11099d 434
af1f0a76
VZ
435 // store off before the standard windows dialog can possibly change it
436 const wxString cwdOrig = wxGetCwd();
437
2bda0e17
KB
438 //== Execute FileDialog >>=================================================
439
c46c1fb8 440 DWORD errCode;
ff3e84ff 441 bool success = DoShowCommFileDialog(&of, m_windowStyle, &errCode);
2bda0e17 442
04227efc 443#ifdef wxTRY_SMALLER_OPENFILENAME
5bb37216
VZ
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 )
f6bcfd97 448 {
5bb37216
VZ
449 of.lStructSize = wxOPENFILENAME_V4_SIZE;
450
ff3e84ff 451 success = DoShowCommFileDialog(&of, m_windowStyle, &errCode);
f6bcfd97 452
5bb37216 453 if ( success || !errCode )
f6bcfd97 454 {
5bb37216
VZ
455 // use this struct size for subsequent dialogs
456 gs_ofStructSize = of.lStructSize;
f6bcfd97
BP
457 }
458 }
04227efc 459#endif // wxTRY_SMALLER_OPENFILENAME
f6bcfd97 460
2bda0e17
KB
461 if ( success )
462 {
5bb37216 463 // GetOpenFileName will always change the current working directory on
c46c1fb8
VZ
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 // wxCHANGE_DIR let's restore the current working directory to what it
467 // was before the dialog was shown.
468 if ( msw_flags & OFN_NOCHANGEDIR )
469 {
470 wxSetWorkingDirectory(cwdOrig);
471 }
472
c61f4f6d
VZ
473 m_fileNames.Empty();
474
ff3e84ff 475 if ( ( m_windowStyle & wxMULTIPLE ) &&
c61f4f6d 476#if defined(OFN_EXPLORER)
c39e82f0 477 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
c61f4f6d 478#else
c39e82f0 479 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
c61f4f6d 480#endif // OFN_EXPLORER
c39e82f0 481 )
c61f4f6d
VZ
482 {
483#if defined(OFN_EXPLORER)
484 m_dir = fileNameBuffer;
485 i = of.nFileOffset;
486 m_fileName = &fileNameBuffer[i];
487 m_fileNames.Add(m_fileName);
488 i += m_fileName.Len() + 1;
489
490 while (fileNameBuffer[i] != wxT('\0'))
491 {
492 m_fileNames.Add(&fileNameBuffer[i]);
493 i += wxStrlen(&fileNameBuffer[i]) + 1;
494 }
495#else
c6603ac2 496 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
c61f4f6d
VZ
497 m_dir = toke.GetNextToken();
498 m_fileName = toke.GetNextToken();
499 m_fileNames.Add(m_fileName);
500
501 while (toke.HasMoreTokens())
502 m_fileNames.Add(toke.GetNextToken());
503#endif // OFN_EXPLORER
504
505 wxString dir(m_dir);
506 if ( m_dir.Last() != _T('\\') )
507 dir += _T('\\');
508
c61f4f6d 509 m_path = dir + m_fileName;
f0f43012 510 m_filterIndex = (int)of.nFilterIndex - 1;
c61f4f6d
VZ
511 }
512 else
513 {
c61f4f6d 514 //=== Adding the correct extension >>=================================
2bda0e17 515
cc42eb7a 516 m_filterIndex = (int)of.nFilterIndex - 1;
2bda0e17 517
0b11099d 518 if ( !of.nFileExtension ||
c6603ac2
VS
519 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
520 {
521 // User has typed a filename without an extension:
f74172ab
VZ
522 const wxChar* extension = filterBuffer;
523 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
2bda0e17 524
f74172ab
VZ
525 for( int i = 0; i < maxFilter; i++ ) // get extension
526 extension = extension + wxStrlen( extension ) + 1;
a039ccbf 527
f74172ab
VZ
528 m_fileName = AppendExtension(fileNameBuffer, extension);
529 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
530 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
2bda0e17 531 }
2bda0e17 532
c61f4f6d
VZ
533 m_path = fileNameBuffer;
534 m_fileName = wxFileNameFromPath(fileNameBuffer);
535 m_fileNames.Add(m_fileName);
536 m_dir = wxPathOnly(fileNameBuffer);
537 }
7cc98b3e 538 }
c46c1fb8 539#ifdef __WXDEBUG__
7cc98b3e
VZ
540 else
541 {
542 // common dialog failed - why?
c46c1fb8 543 if ( errCode != 0 )
e8615999 544 {
c46c1fb8 545 // this msg is only for developers so don't translate it
223d09f6 546 wxLogError(wxT("Common dialog failed with error code %0lx."),
c46c1fb8 547 errCode);
7cc98b3e
VZ
548 }
549 //else: it was just cancelled
7cc98b3e 550 }
c46c1fb8 551#endif // __WXDEBUG__
2bda0e17 552
7cc98b3e 553 return success ? wxID_OK : wxID_CANCEL;
2bda0e17
KB
554
555}
556
3180bc0e 557#endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)
c61f4f6d 558