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