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