get rid of duplicate comment :)
[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 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
401 : GetOpenFileName(&of)) != 0;
402
403 DWORD errCode = CommDlgExtendedError();
404
405 #ifdef __WIN32__
406 if (!success && (errCode == CDERR_STRUCTSIZE))
407 {
408 // The struct size has changed so try a smaller or bigger size
409
410 int oldStructSize = of.lStructSize;
411 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
412 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
413 : (GetOpenFileName(&of) != 0);
414 errCode = CommDlgExtendedError();
415
416 if (!success && (errCode == CDERR_STRUCTSIZE))
417 {
418 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
419 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
420 : (GetOpenFileName(&of) != 0);
421 }
422 }
423 #endif // __WIN32__
424
425 if ( success )
426 {
427 m_fileNames.Empty();
428
429 if ( ( m_dialogStyle & wxMULTIPLE ) &&
430 #if defined(OFN_EXPLORER)
431 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
432 #else
433 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
434 #endif // OFN_EXPLORER
435 )
436 {
437 #if defined(OFN_EXPLORER)
438 m_dir = fileNameBuffer;
439 i = of.nFileOffset;
440 m_fileName = &fileNameBuffer[i];
441 m_fileNames.Add(m_fileName);
442 i += m_fileName.Len() + 1;
443
444 while (fileNameBuffer[i] != wxT('\0'))
445 {
446 m_fileNames.Add(&fileNameBuffer[i]);
447 i += wxStrlen(&fileNameBuffer[i]) + 1;
448 }
449 #else
450 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
451 m_dir = toke.GetNextToken();
452 m_fileName = toke.GetNextToken();
453 m_fileNames.Add(m_fileName);
454
455 while (toke.HasMoreTokens())
456 m_fileNames.Add(toke.GetNextToken());
457 #endif // OFN_EXPLORER
458
459 wxString dir(m_dir);
460 if ( m_dir.Last() != _T('\\') )
461 dir += _T('\\');
462
463 m_path = dir + m_fileName;
464 m_filterIndex = (int)of.nFilterIndex - 1;
465 }
466 else
467 {
468 //=== Adding the correct extension >>=================================
469
470 m_filterIndex = (int)of.nFilterIndex - 1;
471
472 if ( !of.nFileExtension ||
473 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
474 {
475 // User has typed a filename without an extension:
476 const wxChar* extension = filterBuffer;
477 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
478
479 for( int i = 0; i < maxFilter; i++ ) // get extension
480 extension = extension + wxStrlen( extension ) + 1;
481
482 m_fileName = AppendExtension(fileNameBuffer, extension);
483 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
484 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
485 }
486
487 m_path = fileNameBuffer;
488 m_fileName = wxFileNameFromPath(fileNameBuffer);
489 m_fileNames.Add(m_fileName);
490 m_dir = wxPathOnly(fileNameBuffer);
491 }
492 }
493 else
494 {
495 // common dialog failed - why?
496 #ifdef __WXDEBUG__
497 DWORD dwErr = CommDlgExtendedError();
498 if ( dwErr != 0 )
499 {
500 // this msg is only for developers
501 wxLogError(wxT("Common dialog failed with error code %0lx."),
502 dwErr);
503 }
504 //else: it was just cancelled
505 #endif
506 }
507
508 return success ? wxID_OK : wxID_CANCEL;
509
510 }
511
512 #endif // wxUSE_FILEDLG && !(__SMARTPHONE__ && __WXWINCE__)
513