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