OFN_ENABLESIZING not appropriate for WinCE
[wxWidgets.git] / src / msw / filedlg.cpp
1 /////////////////////////////////////////////////////////////////////////////
2 // Name: src/msw/filedlg.cpp
3 // Purpose: wxFileDialog
4 // Author: Julian Smart
5 // Modified by:
6 // Created: 01/02/97
7 // RCS-ID: $Id$
8 // Copyright: (c) Julian Smart
9 // Licence: wxWindows licence
10 /////////////////////////////////////////////////////////////////////////////
11
12 // ============================================================================
13 // declarations
14 // ============================================================================
15
16 // ----------------------------------------------------------------------------
17 // headers
18 // ----------------------------------------------------------------------------
19
20 #if defined(__GNUG__) && !defined(NO_GCC_PRAGMA)
21 #pragma implementation "filedlg.h"
22 #endif
23
24 // For compilers that support precompilation, includes "wx.h".
25 #include "wx/wxprec.h"
26
27 #ifdef __BORLANDC__
28 #pragma hdrstop
29 #endif
30
31 #if wxUSE_FILEDLG && !defined(__SMARTPHONE__)
32
33 #ifndef WX_PRECOMP
34 #include "wx/utils.h"
35 #include "wx/msgdlg.h"
36 #include "wx/filedlg.h"
37 #include "wx/filefn.h"
38 #include "wx/intl.h"
39 #include "wx/log.h"
40 #include "wx/app.h"
41 #endif
42
43 #include "wx/msw/private.h"
44
45 #if !defined(__WIN32__) || defined(__WXWINCE__)
46 #include <commdlg.h>
47 #endif
48
49 #include <math.h>
50 #include <stdlib.h>
51 #include <string.h>
52
53 #include "wx/filename.h"
54 #include "wx/tokenzr.h"
55
56 #ifndef OFN_EXPLORER
57 #define OFN_EXPLORER 0x00080000
58 #endif
59
60 // ----------------------------------------------------------------------------
61 // constants
62 // ----------------------------------------------------------------------------
63
64 #ifdef __WIN32__
65 # define wxMAXPATH 65534
66 #else
67 # define wxMAXPATH 1024
68 #endif
69
70 # define wxMAXFILE 1024
71
72 # define wxMAXEXT 5
73
74 // ----------------------------------------------------------------------------
75 // globals
76 // ----------------------------------------------------------------------------
77
78 // standard dialog size
79 static wxRect gs_rectDialog(0, 0, 428, 266);
80
81 // ============================================================================
82 // implementation
83 // ============================================================================
84
85 IMPLEMENT_CLASS(wxFileDialog, wxFileDialogBase)
86
87 // ----------------------------------------------------------------------------
88 // hook function for moving the dialog
89 // ----------------------------------------------------------------------------
90
91 UINT APIENTRY
92 wxFileDialogHookFunction(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
133 // ----------------------------------------------------------------------------
134 // wxFileDialog
135 // ----------------------------------------------------------------------------
136
137 wxFileDialog::wxFileDialog(wxWindow *parent,
138 const wxString& message,
139 const wxString& defaultDir,
140 const wxString& defaultFileName,
141 const wxString& wildCard,
142 long style,
143 const wxPoint& pos)
144 : wxFileDialogBase(parent, message, defaultDir, defaultFileName,
145 wildCard, style, pos)
146
147 {
148 if ( ( m_dialogStyle & wxMULTIPLE ) && ( m_dialogStyle & wxSAVE ) )
149 m_dialogStyle &= ~wxMULTIPLE;
150
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 }
160 void 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 {
171 if (wxFileName(m_fileNames[n]).IsAbsolute())
172 paths.Add(m_fileNames[n]);
173 else
174 paths.Add(dir + m_fileNames[n]);
175 }
176 }
177
178 void wxFileDialog::GetFilenames(wxArrayString& files) const
179 {
180 files = m_fileNames;
181 }
182
183 void 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
191 void wxFileDialog::DoGetPosition( int *x, int *y ) const
192 {
193 *x = gs_rectDialog.x;
194 *y = gs_rectDialog.y;
195 }
196
197
198 void wxFileDialog::DoGetSize(int *width, int *height) const
199 {
200 *width = gs_rectDialog.width;
201 *height = gs_rectDialog.height;
202 }
203
204 void 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
219 int wxFileDialog::ShowModal()
220 {
221 HWND hWnd = 0;
222 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
223 if (!hWnd && wxTheApp->GetTopWindow())
224 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
225
226 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
227 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
228
229 *fileNameBuffer = wxT('\0');
230 *titleBuffer = wxT('\0');
231
232 #if WXWIN_COMPATIBILITY_2_4
233 long msw_flags = 0;
234 if ( (m_dialogStyle & wxHIDE_READONLY) || (m_dialogStyle & wxSAVE) )
235 msw_flags |= OFN_HIDEREADONLY;
236 #else
237 long msw_flags = OFN_HIDEREADONLY;
238 #endif
239
240 if ( m_dialogStyle & wxFILE_MUST_EXIST )
241 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
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 */
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 }
262
263 if (m_dialogStyle & wxMULTIPLE )
264 {
265 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
266 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
267 }
268
269 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
270 // standard dialog does by default
271 if ( !(m_dialogStyle & wxCHANGE_DIR) )
272 {
273 msw_flags |= OFN_NOCHANGEDIR;
274 }
275
276 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
277 {
278 msw_flags |= OFN_OVERWRITEPROMPT;
279 }
280
281 OPENFILENAME of;
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
292 of.lStructSize = sizeof(OPENFILENAME);
293 #endif
294
295 of.hwndOwner = hWnd;
296 of.lpstrTitle = WXSTRINGCAST m_message;
297 of.lpstrFileTitle = titleBuffer;
298 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
299
300 // Convert forward slashes to backslashes (file selector doesn't like
301 // forward slashes) and also squeeze multiple consecutive slashes into one
302 // as it doesn't like two backslashes in a row neither
303
304 wxString dir;
305 size_t i, len = m_dir.length();
306 dir.reserve(len);
307 for ( i = 0; i < len; i++ )
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
317
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
325 // ignore the next one, unless it is at the start of a UNC path
326 if (i > 0)
327 i++;
328 else
329 break;
330 }
331 // fall through
332
333 default:
334 // normal char
335 dir += ch;
336 }
337 }
338
339 of.lpstrInitialDir = dir.c_str();
340
341 of.Flags = msw_flags;
342 of.lpfnHook = wxFileDialogHookFunction;
343
344 wxArrayString wildDescriptions, wildFilters;
345
346 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
347
348 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
349
350 wxString filterBuffer;
351
352 for (i = 0; i < items ; i++)
353 {
354 filterBuffer += wildDescriptions[i];
355 filterBuffer += wxT("|");
356 filterBuffer += wildFilters[i];
357 filterBuffer += wxT("|");
358 }
359
360 // Replace | with \0
361 for (i = 0; i < filterBuffer.Len(); i++ ) {
362 if ( filterBuffer.GetChar(i) == wxT('|') ) {
363 filterBuffer[i] = wxT('\0');
364 }
365 }
366
367 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
368 of.nFilterIndex = m_filterIndex + 1;
369
370 //=== Setting defaultFileName >>=========================================
371
372 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
373 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
374
375 of.lpstrFile = fileNameBuffer; // holds returned filename
376 of.nMaxFile = wxMAXPATH;
377
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 }
399
400 //== Execute FileDialog >>=================================================
401
402 //== Execute FileDialog >>=================================================
403
404 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
405 : GetOpenFileName(&of)) != 0;
406
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 }
427 #endif // __WIN32__
428
429 if ( success )
430 {
431 m_fileNames.Empty();
432
433 if ( ( m_dialogStyle & wxMULTIPLE ) &&
434 #if defined(OFN_EXPLORER)
435 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
436 #else
437 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
438 #endif // OFN_EXPLORER
439 )
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
454 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
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
467 m_path = dir + m_fileName;
468 m_filterIndex = (int)of.nFilterIndex - 1;
469 }
470 else
471 {
472 //=== Adding the correct extension >>=================================
473
474 m_filterIndex = (int)of.nFilterIndex - 1;
475
476 if ( !of.nFileExtension ||
477 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
478 {
479 // User has typed a filename without an extension:
480 const wxChar* extension = filterBuffer;
481 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
482
483 for( int i = 0; i < maxFilter; i++ ) // get extension
484 extension = extension + wxStrlen( extension ) + 1;
485
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');
489 }
490
491 m_path = fileNameBuffer;
492 m_fileName = wxFileNameFromPath(fileNameBuffer);
493 m_fileNames.Add(m_fileName);
494 m_dir = wxPathOnly(fileNameBuffer);
495 }
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
505 wxLogError(wxT("Common dialog failed with error code %0lx."),
506 dwErr);
507 }
508 //else: it was just cancelled
509 #endif
510 }
511
512 return success ? wxID_OK : wxID_CANCEL;
513
514 }
515
516 #endif // wxUSE_FILEDLG
517