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