]> git.saurik.com Git - wxWidgets.git/blame - src/msw/filedlg.cpp
wxMessageBox off the main thread lost result code.
[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
6c9a19aa 7// Copyright: (c) Julian Smart
65571936 8// Licence: wxWindows licence
2bda0e17
KB
9/////////////////////////////////////////////////////////////////////////////
10
f6bcfd97
BP
11// ============================================================================
12// declarations
13// ============================================================================
14
15// ----------------------------------------------------------------------------
16// headers
17// ----------------------------------------------------------------------------
18
2bda0e17
KB
19// For compilers that support precompilation, includes "wx.h".
20#include "wx/wxprec.h"
21
22#ifdef __BORLANDC__
ba681060 23 #pragma hdrstop
2bda0e17
KB
24#endif
25
3180bc0e 26#if wxUSE_FILEDLG && !(defined(__SMARTPHONE__) && defined(__WXWINCE__))
1e6feb95 27
949c9f74
WS
28#include "wx/filedlg.h"
29
2bda0e17 30#ifndef WX_PRECOMP
57bd4c60
WS
31 #include "wx/msw/wrapcdlg.h"
32 #include "wx/msw/missing.h"
ba681060
VZ
33 #include "wx/utils.h"
34 #include "wx/msgdlg.h"
2b5f62a0 35 #include "wx/filefn.h"
ba681060 36 #include "wx/intl.h"
2662e49e 37 #include "wx/log.h"
f6bcfd97 38 #include "wx/app.h"
18680f86 39 #include "wx/math.h"
8f177c8e 40#endif
2bda0e17 41
2bda0e17
KB
42#include <stdlib.h>
43#include <string.h>
44
30374272 45#include "wx/dynlib.h"
8ad9ca97 46#include "wx/filename.h"
30374272 47#include "wx/scopeguard.h"
8f177c8e 48#include "wx/tokenzr.h"
691745ab 49#include "wx/modalhook.h"
8f177c8e 50
f6bcfd97
BP
51// ----------------------------------------------------------------------------
52// constants
53// ----------------------------------------------------------------------------
54
55#ifdef __WIN32__
2b5f62a0 56# define wxMAXPATH 65534
f6bcfd97
BP
57#else
58# define wxMAXPATH 1024
59#endif
60
61# define wxMAXFILE 1024
62
63# define wxMAXEXT 5
64
0b11099d
VZ
65// ----------------------------------------------------------------------------
66// globals
67// ----------------------------------------------------------------------------
68
0a7b0229 69// standard dialog size for the old Windows systems where the dialog wasn't
d13b34d3 70// resizable
0b11099d
VZ
71static wxRect gs_rectDialog(0, 0, 428, 266);
72
f6bcfd97
BP
73// ============================================================================
74// implementation
75// ============================================================================
76
f74172ab 77IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
2bda0e17 78
30374272
DS
79// ----------------------------------------------------------------------------
80
81namespace
82{
83
84#if wxUSE_DYNLIB_CLASS
85
86typedef BOOL (WINAPI *GetProcessUserModeExceptionPolicy_t)(LPDWORD);
87typedef BOOL (WINAPI *SetProcessUserModeExceptionPolicy_t)(DWORD);
88
89GetProcessUserModeExceptionPolicy_t gs_pfnGetProcessUserModeExceptionPolicy
90 = (GetProcessUserModeExceptionPolicy_t) -1;
91
92SetProcessUserModeExceptionPolicy_t gs_pfnSetProcessUserModeExceptionPolicy
93 = (SetProcessUserModeExceptionPolicy_t) -1;
94
95DWORD gs_oldExceptionPolicyFlags = 0;
96
97bool gs_changedPolicy = false;
98
99#endif // #if wxUSE_DYNLIB_CLASS
100
101/*
102Since Windows 7 by default (callback) exceptions aren't swallowed anymore
103with native x64 applications. Exceptions can occur in a file dialog when
104using the hook procedure in combination with third-party utilities.
105Since Windows 7 SP1 the swallowing of exceptions can be enabled again
106by using SetProcessUserModeExceptionPolicy.
107*/
108void ChangeExceptionPolicy()
109{
110#if wxUSE_DYNLIB_CLASS
111 gs_changedPolicy = false;
112
113 wxLoadedDLL dllKernel32(wxT("kernel32.dll"));
114
115 if ( gs_pfnGetProcessUserModeExceptionPolicy
116 == (GetProcessUserModeExceptionPolicy_t) -1)
117 {
118 wxDL_INIT_FUNC(gs_pfn, GetProcessUserModeExceptionPolicy, dllKernel32);
119 wxDL_INIT_FUNC(gs_pfn, SetProcessUserModeExceptionPolicy, dllKernel32);
120 }
121
122 if ( !gs_pfnGetProcessUserModeExceptionPolicy
123 || !gs_pfnSetProcessUserModeExceptionPolicy
124 || !gs_pfnGetProcessUserModeExceptionPolicy(&gs_oldExceptionPolicyFlags) )
125 {
126 return;
127 }
128
129 if ( gs_pfnSetProcessUserModeExceptionPolicy(gs_oldExceptionPolicyFlags
130 | 0x1 /* PROCESS_CALLBACK_FILTER_ENABLED */ ) )
131 {
132 gs_changedPolicy = true;
133 }
134
135#endif // wxUSE_DYNLIB_CLASS
136}
137
138void RestoreExceptionPolicy()
139{
140#if wxUSE_DYNLIB_CLASS
141 if (gs_changedPolicy)
142 {
143 gs_changedPolicy = false;
144 (void) gs_pfnSetProcessUserModeExceptionPolicy(gs_oldExceptionPolicyFlags);
145 }
146#endif // wxUSE_DYNLIB_CLASS
147}
148
149} // unnamed namespace
150
0b11099d
VZ
151// ----------------------------------------------------------------------------
152// hook function for moving the dialog
153// ----------------------------------------------------------------------------
154
106d80ad 155UINT_PTR APIENTRY
0b11099d
VZ
156wxFileDialogHookFunction(HWND hDlg,
157 UINT iMsg,
158 WPARAM WXUNUSED(wParam),
159 LPARAM lParam)
160{
cb80db46 161 switch ( iMsg )
0b11099d 162 {
d6dae1b4 163#ifndef __WXWINCE__
6fa6d659
VZ
164 case WM_INITDIALOG:
165 {
166 OPENFILENAME* ofn = reinterpret_cast<OPENFILENAME *>(lParam);
167 reinterpret_cast<wxFileDialog *>(ofn->lCustData)
168 ->MSWOnInitDialogHook((WXHWND)hDlg);
169 }
170 break;
d6dae1b4 171#endif // __WXWINCE__
6fa6d659 172
0b11099d
VZ
173 case WM_NOTIFY:
174 {
926df8a1
VZ
175 OFNOTIFY* const
176 pNotifyCode = reinterpret_cast<OFNOTIFY *>(lParam);
177 wxFileDialog* const
178 dialog = reinterpret_cast<wxFileDialog *>(
179 pNotifyCode->lpOFN->lCustData
180 );
181
182 switch ( pNotifyCode->hdr.code )
0b11099d 183 {
926df8a1
VZ
184 case CDN_INITDONE:
185 dialog->MSWOnInitDone((WXHWND)hDlg);
186 break;
187
188 case CDN_SELCHANGE:
189 dialog->MSWOnSelChange((WXHWND)hDlg);
190 break;
191 }
0b11099d
VZ
192 }
193 break;
cb80db46
VZ
194
195 case WM_DESTROY:
196 // reuse the position used for the dialog the next time by default
197 //
198 // NB: at least under Windows 2003 this is useless as after the
199 // first time it's shown the dialog always remembers its size
200 // and position itself and ignores any later SetWindowPos calls
201 wxCopyRECTToRect(wxGetWindowRect(::GetParent(hDlg)), gs_rectDialog);
202 break;
0b11099d
VZ
203 }
204
205 // do the default processing
206 return 0;
207}
208
f6bcfd97 209// ----------------------------------------------------------------------------
b600ed13 210// wxFileDialog
f6bcfd97
BP
211// ----------------------------------------------------------------------------
212
2b5f62a0
VZ
213wxFileDialog::wxFileDialog(wxWindow *parent,
214 const wxString& message,
215 const wxString& defaultDir,
216 const wxString& defaultFileName,
217 const wxString& wildCard,
218 long style,
ff3e84ff
VZ
219 const wxPoint& pos,
220 const wxSize& sz,
221 const wxString& name)
0b11099d 222 : wxFileDialogBase(parent, message, defaultDir, defaultFileName,
ff3e84ff 223 wildCard, style, pos, sz, name)
f74172ab 224
2bda0e17 225{
556151f5 226 // NB: all style checks are done by wxFileDialogBase::Create
2bda0e17 227
0b11099d 228 m_bMovedWindow = false;
0a7b0229 229 m_centreDir = 0;
0b11099d
VZ
230
231 // Must set to zero, otherwise the wx routines won't size the window
232 // the second time you call the file dialog, because it thinks it is
233 // already at the requested size.. (when centering)
234 gs_rectDialog.x =
235 gs_rectDialog.y = 0;
0b11099d 236}
0a7b0229 237
c61f4f6d
VZ
238void wxFileDialog::GetPaths(wxArrayString& paths) const
239{
240 paths.Empty();
241
242 wxString dir(m_dir);
b39badac 243 if ( m_dir.empty() || m_dir.Last() != wxT('\\') )
9a83f860 244 dir += wxT('\\');
c61f4f6d
VZ
245
246 size_t count = m_fileNames.GetCount();
247 for ( size_t n = 0; n < count; n++ )
248 {
8ad9ca97
JS
249 if (wxFileName(m_fileNames[n]).IsAbsolute())
250 paths.Add(m_fileNames[n]);
251 else
252 paths.Add(dir + m_fileNames[n]);
c61f4f6d
VZ
253 }
254}
255
89654c9a
VZ
256void wxFileDialog::GetFilenames(wxArrayString& files) const
257{
258 files = m_fileNames;
259}
260
cb80db46 261void wxFileDialog::DoGetPosition(int *x, int *y) const
0b11099d 262{
cb80db46
VZ
263 if ( x )
264 *x = gs_rectDialog.x;
265 if ( y )
266 *y = gs_rectDialog.y;
0b11099d
VZ
267}
268
0b11099d
VZ
269void wxFileDialog::DoGetSize(int *width, int *height) const
270{
cb80db46
VZ
271 if ( width )
272 *width = gs_rectDialog.width;
273 if ( height )
274 *height = gs_rectDialog.height;
0b11099d
VZ
275}
276
cb80db46 277void wxFileDialog::DoMoveWindow(int x, int y, int WXUNUSED(w), int WXUNUSED(h))
0b11099d 278{
0b11099d
VZ
279 gs_rectDialog.x = x;
280 gs_rectDialog.y = y;
281
0a7b0229
VZ
282 // our HWND is only set when we're called from MSWOnInitDone(), test if
283 // this is the case
284 HWND hwnd = GetHwnd();
285 if ( hwnd )
286 {
287 // size of the dialog can't be changed because the controls are not
288 // laid out correctly then
289 ::SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
290 }
291 else // just remember that we were requested to move the window
292 {
293 m_bMovedWindow = true;
294
295 // if Centre() had been called before, it shouldn't be taken into
296 // account now
297 m_centreDir = 0;
298 }
299}
300
301void wxFileDialog::DoCentre(int dir)
302{
303 m_centreDir = dir;
304 m_bMovedWindow = true;
305
306 // it's unnecessary to do anything else at this stage as we'll redo it in
307 // MSWOnInitDone() anyhow
308}
309
310void wxFileDialog::MSWOnInitDone(WXHWND hDlg)
311{
4c51a665 312 // note the dialog is the parent window: hDlg is a child of it when
0a7b0229
VZ
313 // OFN_EXPLORER is used
314 HWND hFileDlg = ::GetParent((HWND)hDlg);
315
316 // set HWND so that our DoMoveWindow() works correctly
317 SetHWND((WXHWND)hFileDlg);
318
319 if ( m_centreDir )
320 {
321 // now we have the real dialog size, remember it
322 RECT rect;
323 GetWindowRect(hFileDlg, &rect);
324 gs_rectDialog = wxRectFromRECT(rect);
325
326 // and position the window correctly: notice that we must use the base
327 // class version as our own doesn't do anything except setting flags
328 wxFileDialogBase::DoCentre(m_centreDir);
329 }
330 else // need to just move it to the correct place
331 {
332 SetPosition(gs_rectDialog.GetPosition());
333 }
334
926df8a1
VZ
335 // Call selection change handler so that update handler will be
336 // called once with no selection.
337 MSWOnSelChange(hDlg);
338
0a7b0229
VZ
339 // we shouldn't destroy this HWND
340 SetHWND(NULL);
0b11099d
VZ
341}
342
926df8a1
VZ
343void wxFileDialog::MSWOnSelChange(WXHWND hDlg)
344{
345 TCHAR buf[MAX_PATH];
346 LRESULT len = SendMessage(::GetParent(hDlg), CDM_GETFILEPATH,
347 MAX_PATH, reinterpret_cast<LPARAM>(buf));
348
349 if ( len > 0 )
350 m_currentlySelectedFilename = buf;
351 else
352 m_currentlySelectedFilename.clear();
353
354 if ( m_extraControl )
355 m_extraControl->UpdateWindowUI(wxUPDATE_UI_RECURSE);
356}
357
fe048d77
VZ
358// helper used below in ShowCommFileDialog(): style is used to determine
359// whether to show the "Save file" dialog (if it contains wxFD_SAVE bit) or
360// "Open file" one; returns true on success or false on failure in which case
361// err is filled with the CDERR_XXX constant
c46c1fb8
VZ
362static bool DoShowCommFileDialog(OPENFILENAME *of, long style, DWORD *err)
363{
e031f1df 364 if ( style & wxFD_SAVE ? GetSaveFileName(of) : GetOpenFileName(of) )
c46c1fb8
VZ
365 return true;
366
367 if ( err )
368 {
369#ifdef __WXWINCE__
370 // according to MSDN, CommDlgExtendedError() should work under CE as
371 // well but apparently in practice it doesn't (anybody has more
372 // details?)
373 *err = GetLastError();
374#else
375 *err = CommDlgExtendedError();
376#endif
377 }
378
379 return false;
380}
381
04227efc
VZ
382// We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
383// know if the OPENFILENAME declared in the currently used headers is a V5 or
384// V4 (smaller) one so we try to manually extend the struct in case it is the
385// old one.
5bb37216 386//
04227efc
VZ
387// We don't do this on Windows CE nor under Win64, however, as there are no
388// compilers with old headers for these architectures
389#if defined(__WXWINCE__) || defined(__WIN64__)
5bb37216
VZ
390 typedef OPENFILENAME wxOPENFILENAME;
391
04227efc
VZ
392 static const DWORD gs_ofStructSize = sizeof(OPENFILENAME);
393#else // !__WXWINCE__ || __WIN64__
394 #define wxTRY_SMALLER_OPENFILENAME
395
5bb37216
VZ
396 struct wxOPENFILENAME : public OPENFILENAME
397 {
398 // fields added in Windows 2000/XP comdlg32.dll version
399 void *pVoid;
400 DWORD dw1;
401 DWORD dw2;
402 };
403
404 // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
405 // because sizeof(OPENFILENAME) in the headers we use when compiling the
406 // library could be less if _WIN32_WINNT is not >= 0x500
407 static const DWORD wxOPENFILENAME_V5_SIZE = 88;
408
409 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
410 static const DWORD wxOPENFILENAME_V4_SIZE = 76;
5bb37216 411
04227efc
VZ
412 // always try the new one first
413 static DWORD gs_ofStructSize = wxOPENFILENAME_V5_SIZE;
414#endif // __WXWINCE__ || __WIN64__/!...
5bb37216 415
fe048d77
VZ
416static bool ShowCommFileDialog(OPENFILENAME *of, long style)
417{
418 DWORD errCode;
419 bool success = DoShowCommFileDialog(of, style, &errCode);
420
421#ifdef wxTRY_SMALLER_OPENFILENAME
422 // the system might be too old to support the new version file dialog
423 // boxes, try with the old size
424 if ( !success && errCode == CDERR_STRUCTSIZE &&
425 of->lStructSize != wxOPENFILENAME_V4_SIZE )
426 {
427 of->lStructSize = wxOPENFILENAME_V4_SIZE;
428
429 success = DoShowCommFileDialog(of, style, &errCode);
430
431 if ( success || !errCode )
432 {
433 // use this struct size for subsequent dialogs
434 gs_ofStructSize = of->lStructSize;
435 }
436 }
437#endif // wxTRY_SMALLER_OPENFILENAME
438
d4380371
VZ
439 if ( !success &&
440 // FNERR_INVALIDFILENAME is not defined under CE (besides we don't
441 // use CommDlgExtendedError() there anyhow)
442#ifndef __WXWINCE__
443 errCode == FNERR_INVALIDFILENAME &&
444#endif // !__WXWINCE__
445 of->lpstrFile[0] )
fe048d77
VZ
446 {
447 // this can happen if the default file name is invalid, try without it
448 // now
9a83f860 449 of->lpstrFile[0] = wxT('\0');
fe048d77
VZ
450 success = DoShowCommFileDialog(of, style, &errCode);
451 }
452
453 if ( !success )
454 {
455 // common dialog failed - why?
456 if ( errCode != 0 )
457 {
458 wxLogError(_("File dialog failed with error code %0lx."), errCode);
459 }
460 //else: it was just cancelled
461
462 return false;
463 }
464
465 return true;
466}
467
d6dae1b4 468#ifndef __WXWINCE__
6fa6d659
VZ
469void wxFileDialog::MSWOnInitDialogHook(WXHWND hwnd)
470{
471 SetHWND(hwnd);
472
473 CreateExtraControl();
474
475 SetHWND(NULL);
476}
d6dae1b4 477#endif // __WXWINCE__
6fa6d659 478
c61f4f6d 479int wxFileDialog::ShowModal()
2bda0e17 480{
691745ab 481 WX_HOOK_MODAL_DIALOG();
643e9cf9 482
1f2f0331
VZ
483 HWND hWnd = 0;
484 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
f6bcfd97
BP
485 if (!hWnd && wxTheApp->GetTopWindow())
486 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
2bda0e17 487
f6bcfd97
BP
488 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
489 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
2bda0e17 490
223d09f6
KB
491 *fileNameBuffer = wxT('\0');
492 *titleBuffer = wxT('\0');
2bda0e17 493
21416306 494 long msw_flags = OFN_HIDEREADONLY;
21416306 495
b014db05 496 if ( HasFdFlag(wxFD_FILE_MUST_EXIST) )
1f2f0331 497 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
0b11099d
VZ
498 /*
499 If the window has been moved the programmer is probably
500 trying to center or position it. Thus we set the callback
501 or hook function so that we can actually adjust the position.
502 Without moving or centering the dlg, it will just stay
503 in the upper left of the frame, it does not center
5bb37216 504 automatically.
0b11099d 505 */
6fa6d659 506 if (m_bMovedWindow || HasExtraControlCreator()) // we need these flags.
503528dc 507 {
30374272 508 ChangeExceptionPolicy();
503528dc
JS
509 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK;
510#ifndef __WXWINCE__
511 msw_flags |= OFN_ENABLESIZING;
512#endif
513 }
6e8aa701 514
30374272
DS
515 wxON_BLOCK_EXIT0(RestoreExceptionPolicy);
516
b014db05 517 if ( HasFdFlag(wxFD_MULTIPLE) )
6e8aa701
VZ
518 {
519 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
520 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
521 }
522
e031f1df 523 // if wxFD_CHANGE_DIR flag is not given we shouldn't change the CWD which the
af1f0a76
VZ
524 // standard dialog does by default (notice that under NT it does it anyhow,
525 // OFN_NOCHANGEDIR or not, see below)
b014db05 526 if ( !HasFdFlag(wxFD_CHANGE_DIR) )
6e8aa701
VZ
527 {
528 msw_flags |= OFN_NOCHANGEDIR;
529 }
ac95e671 530
b014db05 531 if ( HasFdFlag(wxFD_OVERWRITE_PROMPT) )
99d1b93d
VZ
532 {
533 msw_flags |= OFN_OVERWRITEPROMPT;
534 }
ac95e671 535
5bb37216 536 wxOPENFILENAME of;
f6bcfd97
BP
537 wxZeroMemory(of);
538
5bb37216 539 of.lStructSize = gs_ofStructSize;
e15e548b 540 of.hwndOwner = hWnd;
017dc06b 541 of.lpstrTitle = m_message.t_str();
e15e548b 542 of.lpstrFileTitle = titleBuffer;
5bb37216 543 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT;
2bda0e17 544
6fa6d659
VZ
545#ifndef __WXWINCE__
546 GlobalPtr hgbl;
547 if ( HasExtraControlCreator() )
548 {
549 msw_flags |= OFN_ENABLETEMPLATEHANDLE;
550
551 hgbl.Init(256, GMEM_ZEROINIT);
552 GlobalPtrLock hgblLock(hgbl);
553 LPDLGTEMPLATE lpdt = static_cast<LPDLGTEMPLATE>(hgblLock.Get());
554
555 // Define a dialog box.
556
557 lpdt->style = DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS;
558 lpdt->cdit = 0; // Number of controls
559 lpdt->x = 0;
560 lpdt->y = 0;
561
c10c791b
VZ
562 // convert the size of the extra controls to the dialog units
563 const wxSize extraSize = GetExtraControlSize();
564 const LONG baseUnits = ::GetDialogBaseUnits();
565 lpdt->cx = ::MulDiv(extraSize.x, 4, LOWORD(baseUnits));
566 lpdt->cy = ::MulDiv(extraSize.y, 8, HIWORD(baseUnits));
6fa6d659
VZ
567
568 // after the DLGTEMPLATE there are 3 additional WORDs for dialog menu,
569 // class and title, all three set to zeros.
570
571 of.hInstance = (HINSTANCE)lpdt;
572 }
573#endif // __WXWINCE__
574
0bc9b25e 575 // Convert forward slashes to backslashes (file selector doesn't like
99d1b93d
VZ
576 // forward slashes) and also squeeze multiple consecutive slashes into one
577 // as it doesn't like two backslashes in a row neither
0627d091 578
cbe874bd
WS
579 wxString dir;
580 size_t i, len = m_dir.length();
99d1b93d 581 dir.reserve(len);
0627d091 582 for ( i = 0; i < len; i++ )
99d1b93d
VZ
583 {
584 wxChar ch = m_dir[i];
585 switch ( ch )
586 {
9a83f860 587 case wxT('/'):
99d1b93d 588 // convert to backslash
9a83f860 589 ch = wxT('\\');
99d1b93d
VZ
590
591 // fall through
0bc9b25e 592
9a83f860 593 case wxT('\\'):
99d1b93d
VZ
594 while ( i < len - 1 )
595 {
596 wxChar chNext = m_dir[i + 1];
9a83f860 597 if ( chNext != wxT('\\') && chNext != wxT('/') )
99d1b93d
VZ
598 break;
599
04d93c3a
CE
600 // ignore the next one, unless it is at the start of a UNC path
601 if (i > 0)
602 i++;
603 else
0b11099d 604 break;
99d1b93d
VZ
605 }
606 // fall through
607
608 default:
609 // normal char
610 dir += ch;
611 }
612 }
613
614 of.lpstrInitialDir = dir.c_str();
2bda0e17 615
e15e548b 616 of.Flags = msw_flags;
0b11099d 617 of.lpfnHook = wxFileDialogHookFunction;
0a7b0229 618 of.lCustData = (LPARAM)this;
2bda0e17 619
daf32463 620 wxArrayString wildDescriptions, wildFilters;
2bda0e17 621
daf32463 622 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
2bda0e17 623
9a83f860 624 wxASSERT_MSG( items > 0 , wxT("empty wildcard list") );
2bda0e17 625
1f2f0331 626 wxString filterBuffer;
2bda0e17 627
daf32463
WS
628 for (i = 0; i < items ; i++)
629 {
630 filterBuffer += wildDescriptions[i];
631 filterBuffer += wxT("|");
632 filterBuffer += wildFilters[i];
633 filterBuffer += wxT("|");
574c0bbf
JS
634 }
635
574c0bbf 636 // Replace | with \0
e031f1df 637 for (i = 0; i < filterBuffer.length(); i++ ) {
223d09f6
KB
638 if ( filterBuffer.GetChar(i) == wxT('|') ) {
639 filterBuffer[i] = wxT('\0');
e15e548b
VZ
640 }
641 }
2bda0e17 642
017dc06b 643 of.lpstrFilter = filterBuffer.t_str();
cc42eb7a 644 of.nFilterIndex = m_filterIndex + 1;
2bda0e17
KB
645
646 //=== Setting defaultFileName >>=========================================
647
64accea5 648 wxStrlcpy(fileNameBuffer, m_fileName.c_str(), WXSIZEOF(fileNameBuffer));
2bda0e17 649
e15e548b 650 of.lpstrFile = fileNameBuffer; // holds returned filename
f6bcfd97 651 of.nMaxFile = wxMAXPATH;
2bda0e17 652
90bddb85 653 // we must set the default extension because otherwise Windows would check
e031f1df 654 // for the existing of a wrong file with wxFD_OVERWRITE_PROMPT (i.e. if the
90bddb85
VZ
655 // user types "foo" and the default extension is ".bar" we should force it
656 // to check for "foo.bar" existence and not "foo")
657 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
b014db05 658 if (HasFdFlag(wxFD_SAVE))
90bddb85 659 {
017dc06b 660 const wxChar* extension = filterBuffer.t_str();
90bddb85
VZ
661 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
662
663 for( int i = 0; i < maxFilter; i++ ) // get extension
664 extension = extension + wxStrlen( extension ) + 1;
665
666 // use dummy name a to avoid assert in AppendExtension
667 defextBuffer = AppendExtension(wxT("a"), extension);
668 if (defextBuffer.StartsWith(wxT("a.")))
669 {
026ff75b 670 defextBuffer = defextBuffer.Mid(2); // remove "a."
90bddb85
VZ
671 of.lpstrDefExt = defextBuffer.c_str();
672 }
673 }
0b11099d 674
af1f0a76
VZ
675 // store off before the standard windows dialog can possibly change it
676 const wxString cwdOrig = wxGetCwd();
677
2bda0e17
KB
678 //== Execute FileDialog >>=================================================
679
fe048d77
VZ
680 if ( !ShowCommFileDialog(&of, m_windowStyle) )
681 return wxID_CANCEL;
2bda0e17 682
fe048d77
VZ
683 // GetOpenFileName will always change the current working directory on
684 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
685 // OFN_NOCHANGEDIR has no effect. If the user did not specify
686 // wxFD_CHANGE_DIR let's restore the current working directory to what it
687 // was before the dialog was shown.
688 if ( msw_flags & OFN_NOCHANGEDIR )
f6bcfd97 689 {
fe048d77 690 wxSetWorkingDirectory(cwdOrig);
f6bcfd97 691 }
c46c1fb8 692
fe048d77 693 m_fileNames.Empty();
c61f4f6d 694
fe048d77 695 if ( ( HasFdFlag(wxFD_MULTIPLE) ) &&
c61f4f6d 696#if defined(OFN_EXPLORER)
fe048d77 697 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
c61f4f6d 698#else
fe048d77 699 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
c61f4f6d 700#endif // OFN_EXPLORER
fe048d77
VZ
701 )
702 {
c61f4f6d 703#if defined(OFN_EXPLORER)
fe048d77
VZ
704 m_dir = fileNameBuffer;
705 i = of.nFileOffset;
706 m_fileName = &fileNameBuffer[i];
707 m_fileNames.Add(m_fileName);
708 i += m_fileName.length() + 1;
c61f4f6d 709
fe048d77
VZ
710 while (fileNameBuffer[i] != wxT('\0'))
711 {
712 m_fileNames.Add(&fileNameBuffer[i]);
713 i += wxStrlen(&fileNameBuffer[i]) + 1;
714 }
c61f4f6d 715#else
9a83f860 716 wxStringTokenizer toke(fileNameBuffer, wxT(" \t\r\n"));
fe048d77
VZ
717 m_dir = toke.GetNextToken();
718 m_fileName = toke.GetNextToken();
719 m_fileNames.Add(m_fileName);
c61f4f6d 720
fe048d77
VZ
721 while (toke.HasMoreTokens())
722 m_fileNames.Add(toke.GetNextToken());
c61f4f6d
VZ
723#endif // OFN_EXPLORER
724
fe048d77 725 wxString dir(m_dir);
9a83f860
VZ
726 if ( m_dir.Last() != wxT('\\') )
727 dir += wxT('\\');
2bda0e17 728
fe048d77
VZ
729 m_path = dir + m_fileName;
730 m_filterIndex = (int)of.nFilterIndex - 1;
731 }
732 else
733 {
734 //=== Adding the correct extension >>=================================
2bda0e17 735
fe048d77 736 m_filterIndex = (int)of.nFilterIndex - 1;
2bda0e17 737
fe048d77
VZ
738 if ( !of.nFileExtension ||
739 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
740 {
741 // User has typed a filename without an extension:
017dc06b 742 const wxChar* extension = filterBuffer.t_str();
fe048d77 743 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
a039ccbf 744
fe048d77
VZ
745 for( int i = 0; i < maxFilter; i++ ) // get extension
746 extension = extension + wxStrlen( extension ) + 1;
2bda0e17 747
fe048d77 748 m_fileName = AppendExtension(fileNameBuffer, extension);
e408bf52 749 wxStrlcpy(fileNameBuffer, m_fileName.c_str(), WXSIZEOF(fileNameBuffer));
c61f4f6d 750 }
fe048d77
VZ
751
752 m_path = fileNameBuffer;
753 m_fileName = wxFileNameFromPath(fileNameBuffer);
754 m_fileNames.Add(m_fileName);
755 m_dir = wxPathOnly(fileNameBuffer);
7cc98b3e 756 }
2bda0e17 757
fe048d77 758 return wxID_OK;
2bda0e17
KB
759
760}
761
3180bc0e 762#endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)