]> git.saurik.com Git - wxWidgets.git/blame - src/msw/filedlg.cpp
added InitCommandEventWithItems() and call it from wxChoice and wxComboBox code to...
[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,
f74172ab 132 const wxPoint& pos)
0b11099d
VZ
133 : wxFileDialogBase(parent, message, defaultDir, defaultFileName,
134 wildCard, style, pos)
f74172ab 135
2bda0e17 136{
c61f4f6d
VZ
137 if ( ( m_dialogStyle & wxMULTIPLE ) && ( m_dialogStyle & wxSAVE ) )
138 m_dialogStyle &= ~wxMULTIPLE;
2bda0e17 139
0b11099d
VZ
140 m_bMovedWindow = false;
141
142 // Must set to zero, otherwise the wx routines won't size the window
143 // the second time you call the file dialog, because it thinks it is
144 // already at the requested size.. (when centering)
145 gs_rectDialog.x =
146 gs_rectDialog.y = 0;
147
148}
c61f4f6d
VZ
149void wxFileDialog::GetPaths(wxArrayString& paths) const
150{
151 paths.Empty();
152
153 wxString dir(m_dir);
154 if ( m_dir.Last() != _T('\\') )
155 dir += _T('\\');
156
157 size_t count = m_fileNames.GetCount();
158 for ( size_t n = 0; n < count; n++ )
159 {
8ad9ca97
JS
160 if (wxFileName(m_fileNames[n]).IsAbsolute())
161 paths.Add(m_fileNames[n]);
162 else
163 paths.Add(dir + m_fileNames[n]);
c61f4f6d
VZ
164 }
165}
166
89654c9a
VZ
167void wxFileDialog::GetFilenames(wxArrayString& files) const
168{
169 files = m_fileNames;
170}
171
2b5f62a0
VZ
172void wxFileDialog::SetPath(const wxString& path)
173{
174 wxString ext;
175 wxSplitPath(path, &m_dir, &m_fileName, &ext);
176 if ( !ext.empty() )
177 m_fileName << _T('.') << ext;
178}
179
cb80db46 180void wxFileDialog::DoGetPosition(int *x, int *y) const
0b11099d 181{
cb80db46
VZ
182 if ( x )
183 *x = gs_rectDialog.x;
184 if ( y )
185 *y = gs_rectDialog.y;
0b11099d
VZ
186}
187
188
189void wxFileDialog::DoGetSize(int *width, int *height) const
190{
cb80db46
VZ
191 if ( width )
192 *width = gs_rectDialog.width;
193 if ( height )
194 *height = gs_rectDialog.height;
0b11099d
VZ
195}
196
cb80db46 197void wxFileDialog::DoMoveWindow(int x, int y, int WXUNUSED(w), int WXUNUSED(h))
0b11099d
VZ
198{
199 m_bMovedWindow = true;
200
201 gs_rectDialog.x = x;
202 gs_rectDialog.y = y;
203
cb80db46
VZ
204 // size of the dialog can't be changed because the controls are not laid
205 // out correctly then
0b11099d
VZ
206}
207
c46c1fb8
VZ
208// helper used below in ShowModal(): style is used to determine whether to show
209// the "Save file" dialog (if it contains wxSAVE bit) or "Open file" one;
210// returns true on success or false on failure in which case err is filled with
211// the CDERR_XXX constant
212static bool DoShowCommFileDialog(OPENFILENAME *of, long style, DWORD *err)
213{
214 if ( style & wxSAVE ? GetSaveFileName(of) : GetOpenFileName(of) )
215 return true;
216
217 if ( err )
218 {
219#ifdef __WXWINCE__
220 // according to MSDN, CommDlgExtendedError() should work under CE as
221 // well but apparently in practice it doesn't (anybody has more
222 // details?)
223 *err = GetLastError();
224#else
225 *err = CommDlgExtendedError();
226#endif
227 }
228
229 return false;
230}
231
04227efc
VZ
232// We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
233// know if the OPENFILENAME declared in the currently used headers is a V5 or
234// V4 (smaller) one so we try to manually extend the struct in case it is the
235// old one.
5bb37216 236//
04227efc
VZ
237// We don't do this on Windows CE nor under Win64, however, as there are no
238// compilers with old headers for these architectures
239#if defined(__WXWINCE__) || defined(__WIN64__)
5bb37216
VZ
240 typedef OPENFILENAME wxOPENFILENAME;
241
04227efc
VZ
242 static const DWORD gs_ofStructSize = sizeof(OPENFILENAME);
243#else // !__WXWINCE__ || __WIN64__
244 #define wxTRY_SMALLER_OPENFILENAME
245
5bb37216
VZ
246 struct wxOPENFILENAME : public OPENFILENAME
247 {
248 // fields added in Windows 2000/XP comdlg32.dll version
249 void *pVoid;
250 DWORD dw1;
251 DWORD dw2;
252 };
253
254 // hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
255 // because sizeof(OPENFILENAME) in the headers we use when compiling the
256 // library could be less if _WIN32_WINNT is not >= 0x500
257 static const DWORD wxOPENFILENAME_V5_SIZE = 88;
258
259 // this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
260 static const DWORD wxOPENFILENAME_V4_SIZE = 76;
5bb37216 261
04227efc
VZ
262 // always try the new one first
263 static DWORD gs_ofStructSize = wxOPENFILENAME_V5_SIZE;
264#endif // __WXWINCE__ || __WIN64__/!...
5bb37216 265
c61f4f6d 266int wxFileDialog::ShowModal()
2bda0e17 267{
1f2f0331
VZ
268 HWND hWnd = 0;
269 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
f6bcfd97
BP
270 if (!hWnd && wxTheApp->GetTopWindow())
271 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
2bda0e17 272
f6bcfd97
BP
273 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
274 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
2bda0e17 275
223d09f6
KB
276 *fileNameBuffer = wxT('\0');
277 *titleBuffer = wxT('\0');
2bda0e17 278
21416306 279#if WXWIN_COMPATIBILITY_2_4
2bda0e17 280 long msw_flags = 0;
e15e548b 281 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
1f2f0331 282 msw_flags |= OFN_HIDEREADONLY;
21416306
WS
283#else
284 long msw_flags = OFN_HIDEREADONLY;
285#endif
286
e15e548b 287 if ( m_dialogStyle & wxFILE_MUST_EXIST )
1f2f0331 288 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
0b11099d
VZ
289 /*
290 If the window has been moved the programmer is probably
291 trying to center or position it. Thus we set the callback
292 or hook function so that we can actually adjust the position.
293 Without moving or centering the dlg, it will just stay
294 in the upper left of the frame, it does not center
5bb37216 295 automatically.
0b11099d 296 */
503528dc
JS
297 if (m_bMovedWindow) // we need these flags.
298 {
299 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK;
300#ifndef __WXWINCE__
301 msw_flags |= OFN_ENABLESIZING;
302#endif
303 }
6e8aa701 304
c61f4f6d 305 if (m_dialogStyle & wxMULTIPLE )
6e8aa701
VZ
306 {
307 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
308 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
309 }
310
99d1b93d 311 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
af1f0a76
VZ
312 // standard dialog does by default (notice that under NT it does it anyhow,
313 // OFN_NOCHANGEDIR or not, see below)
6e8aa701
VZ
314 if ( !(m_dialogStyle & wxCHANGE_DIR) )
315 {
316 msw_flags |= OFN_NOCHANGEDIR;
317 }
ac95e671 318
99d1b93d
VZ
319 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
320 {
321 msw_flags |= OFN_OVERWRITEPROMPT;
322 }
ac95e671 323
5bb37216 324 wxOPENFILENAME of;
f6bcfd97
BP
325 wxZeroMemory(of);
326
5bb37216 327 of.lStructSize = gs_ofStructSize;
e15e548b 328 of.hwndOwner = hWnd;
837e5743 329 of.lpstrTitle = WXSTRINGCAST m_message;
e15e548b 330 of.lpstrFileTitle = titleBuffer;
5bb37216 331 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT;
2bda0e17 332
0bc9b25e 333 // Convert forward slashes to backslashes (file selector doesn't like
99d1b93d
VZ
334 // forward slashes) and also squeeze multiple consecutive slashes into one
335 // as it doesn't like two backslashes in a row neither
0627d091 336
cbe874bd
WS
337 wxString dir;
338 size_t i, len = m_dir.length();
99d1b93d 339 dir.reserve(len);
0627d091 340 for ( i = 0; i < len; i++ )
99d1b93d
VZ
341 {
342 wxChar ch = m_dir[i];
343 switch ( ch )
344 {
345 case _T('/'):
346 // convert to backslash
347 ch = _T('\\');
348
349 // fall through
0bc9b25e 350
99d1b93d
VZ
351 case _T('\\'):
352 while ( i < len - 1 )
353 {
354 wxChar chNext = m_dir[i + 1];
355 if ( chNext != _T('\\') && chNext != _T('/') )
356 break;
357
04d93c3a
CE
358 // ignore the next one, unless it is at the start of a UNC path
359 if (i > 0)
360 i++;
361 else
0b11099d 362 break;
99d1b93d
VZ
363 }
364 // fall through
365
366 default:
367 // normal char
368 dir += ch;
369 }
370 }
371
372 of.lpstrInitialDir = dir.c_str();
2bda0e17 373
e15e548b 374 of.Flags = msw_flags;
0b11099d 375 of.lpfnHook = wxFileDialogHookFunction;
2bda0e17 376
daf32463 377 wxArrayString wildDescriptions, wildFilters;
2bda0e17 378
daf32463 379 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
2bda0e17 380
daf32463 381 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
2bda0e17 382
1f2f0331 383 wxString filterBuffer;
2bda0e17 384
daf32463
WS
385 for (i = 0; i < items ; i++)
386 {
387 filterBuffer += wildDescriptions[i];
388 filterBuffer += wxT("|");
389 filterBuffer += wildFilters[i];
390 filterBuffer += wxT("|");
574c0bbf
JS
391 }
392
574c0bbf 393 // Replace | with \0
0bc9b25e 394 for (i = 0; i < filterBuffer.Len(); i++ ) {
223d09f6
KB
395 if ( filterBuffer.GetChar(i) == wxT('|') ) {
396 filterBuffer[i] = wxT('\0');
e15e548b
VZ
397 }
398 }
2bda0e17 399
daf32463 400 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
cc42eb7a 401 of.nFilterIndex = m_filterIndex + 1;
2bda0e17
KB
402
403 //=== Setting defaultFileName >>=========================================
404
f6bcfd97
BP
405 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
406 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
2bda0e17 407
e15e548b 408 of.lpstrFile = fileNameBuffer; // holds returned filename
f6bcfd97 409 of.nMaxFile = wxMAXPATH;
2bda0e17 410
90bddb85
VZ
411 // we must set the default extension because otherwise Windows would check
412 // for the existing of a wrong file with wxOVERWRITE_PROMPT (i.e. if the
413 // user types "foo" and the default extension is ".bar" we should force it
414 // to check for "foo.bar" existence and not "foo")
415 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
416 if (m_dialogStyle & wxSAVE)
417 {
418 const wxChar* extension = filterBuffer;
419 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
420
421 for( int i = 0; i < maxFilter; i++ ) // get extension
422 extension = extension + wxStrlen( extension ) + 1;
423
424 // use dummy name a to avoid assert in AppendExtension
425 defextBuffer = AppendExtension(wxT("a"), extension);
426 if (defextBuffer.StartsWith(wxT("a.")))
427 {
428 defextBuffer.Mid(2);
429 of.lpstrDefExt = defextBuffer.c_str();
430 }
431 }
0b11099d 432
af1f0a76
VZ
433 // store off before the standard windows dialog can possibly change it
434 const wxString cwdOrig = wxGetCwd();
435
2bda0e17
KB
436 //== Execute FileDialog >>=================================================
437
c46c1fb8
VZ
438 DWORD errCode;
439 bool success = DoShowCommFileDialog(&of, m_dialogStyle, &errCode);
2bda0e17 440
04227efc 441#ifdef wxTRY_SMALLER_OPENFILENAME
5bb37216
VZ
442 // the system might be too old to support the new version file dialog
443 // boxes, try with the old size
444 if ( !success && errCode == CDERR_STRUCTSIZE &&
445 of.lStructSize != wxOPENFILENAME_V4_SIZE )
f6bcfd97 446 {
5bb37216
VZ
447 of.lStructSize = wxOPENFILENAME_V4_SIZE;
448
c46c1fb8 449 success = DoShowCommFileDialog(&of, m_dialogStyle, &errCode);
f6bcfd97 450
5bb37216 451 if ( success || !errCode )
f6bcfd97 452 {
5bb37216
VZ
453 // use this struct size for subsequent dialogs
454 gs_ofStructSize = of.lStructSize;
f6bcfd97
BP
455 }
456 }
04227efc 457#endif // wxTRY_SMALLER_OPENFILENAME
f6bcfd97 458
2bda0e17
KB
459 if ( success )
460 {
5bb37216 461 // GetOpenFileName will always change the current working directory on
c46c1fb8
VZ
462 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
463 // OFN_NOCHANGEDIR has no effect. If the user did not specify
464 // wxCHANGE_DIR let's restore the current working directory to what it
465 // was before the dialog was shown.
466 if ( msw_flags & OFN_NOCHANGEDIR )
467 {
468 wxSetWorkingDirectory(cwdOrig);
469 }
470
c61f4f6d
VZ
471 m_fileNames.Empty();
472
473 if ( ( m_dialogStyle & wxMULTIPLE ) &&
474#if defined(OFN_EXPLORER)
c39e82f0 475 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
c61f4f6d 476#else
c39e82f0 477 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
c61f4f6d 478#endif // OFN_EXPLORER
c39e82f0 479 )
c61f4f6d
VZ
480 {
481#if defined(OFN_EXPLORER)
482 m_dir = fileNameBuffer;
483 i = of.nFileOffset;
484 m_fileName = &fileNameBuffer[i];
485 m_fileNames.Add(m_fileName);
486 i += m_fileName.Len() + 1;
487
488 while (fileNameBuffer[i] != wxT('\0'))
489 {
490 m_fileNames.Add(&fileNameBuffer[i]);
491 i += wxStrlen(&fileNameBuffer[i]) + 1;
492 }
493#else
c6603ac2 494 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
c61f4f6d
VZ
495 m_dir = toke.GetNextToken();
496 m_fileName = toke.GetNextToken();
497 m_fileNames.Add(m_fileName);
498
499 while (toke.HasMoreTokens())
500 m_fileNames.Add(toke.GetNextToken());
501#endif // OFN_EXPLORER
502
503 wxString dir(m_dir);
504 if ( m_dir.Last() != _T('\\') )
505 dir += _T('\\');
506
c61f4f6d 507 m_path = dir + m_fileName;
f0f43012 508 m_filterIndex = (int)of.nFilterIndex - 1;
c61f4f6d
VZ
509 }
510 else
511 {
c61f4f6d 512 //=== Adding the correct extension >>=================================
2bda0e17 513
cc42eb7a 514 m_filterIndex = (int)of.nFilterIndex - 1;
2bda0e17 515
0b11099d 516 if ( !of.nFileExtension ||
c6603ac2
VS
517 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
518 {
519 // User has typed a filename without an extension:
f74172ab
VZ
520 const wxChar* extension = filterBuffer;
521 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
2bda0e17 522
f74172ab
VZ
523 for( int i = 0; i < maxFilter; i++ ) // get extension
524 extension = extension + wxStrlen( extension ) + 1;
a039ccbf 525
f74172ab
VZ
526 m_fileName = AppendExtension(fileNameBuffer, extension);
527 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
528 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
2bda0e17 529 }
2bda0e17 530
c61f4f6d
VZ
531 m_path = fileNameBuffer;
532 m_fileName = wxFileNameFromPath(fileNameBuffer);
533 m_fileNames.Add(m_fileName);
534 m_dir = wxPathOnly(fileNameBuffer);
535 }
7cc98b3e 536 }
c46c1fb8 537#ifdef __WXDEBUG__
7cc98b3e
VZ
538 else
539 {
540 // common dialog failed - why?
c46c1fb8 541 if ( errCode != 0 )
e8615999 542 {
c46c1fb8 543 // this msg is only for developers so don't translate it
223d09f6 544 wxLogError(wxT("Common dialog failed with error code %0lx."),
c46c1fb8 545 errCode);
7cc98b3e
VZ
546 }
547 //else: it was just cancelled
7cc98b3e 548 }
c46c1fb8 549#endif // __WXDEBUG__
2bda0e17 550
7cc98b3e 551 return success ? wxID_OK : wxID_CANCEL;
2bda0e17
KB
552
553}
554
3180bc0e 555#endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)
c61f4f6d 556