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