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