implemented moving the dialog (patch 996448)
[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 the these flags.
256 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK|OFN_ENABLESIZING;
257
258 if (m_dialogStyle & wxMULTIPLE )
259 {
260 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
261 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
262 }
263
264 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
265 // standard dialog does by default
266 if ( !(m_dialogStyle & wxCHANGE_DIR) )
267 {
268 msw_flags |= OFN_NOCHANGEDIR;
269 }
270
271 if ( m_dialogStyle & wxOVERWRITE_PROMPT )
272 {
273 msw_flags |= OFN_OVERWRITEPROMPT;
274 }
275
276 OPENFILENAME of;
277 wxZeroMemory(of);
278
279 // the OPENFILENAME struct has been extended in newer version of
280 // comcdlg32.dll, but as we don't use the extended fields anyhow, set
281 // the struct size to the old value - otherwise, the programs compiled
282 // with new headers will not work with the old libraries
283 #if defined(_WIN32_WINNT) && (_WIN32_WINNT >= 0x0500)
284 of.lStructSize = sizeof(OPENFILENAME) -
285 (sizeof(void *) + 2*sizeof(DWORD));
286 #else // old headers
287 of.lStructSize = sizeof(OPENFILENAME);
288 #endif
289
290 of.hwndOwner = hWnd;
291 of.lpstrTitle = WXSTRINGCAST m_message;
292 of.lpstrFileTitle = titleBuffer;
293 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT; // Windows 3.0 and 3.1
294
295 // Convert forward slashes to backslashes (file selector doesn't like
296 // forward slashes) and also squeeze multiple consecutive slashes into one
297 // as it doesn't like two backslashes in a row neither
298
299 wxString dir;
300 size_t i, len = m_dir.length();
301 dir.reserve(len);
302 for ( i = 0; i < len; i++ )
303 {
304 wxChar ch = m_dir[i];
305 switch ( ch )
306 {
307 case _T('/'):
308 // convert to backslash
309 ch = _T('\\');
310
311 // fall through
312
313 case _T('\\'):
314 while ( i < len - 1 )
315 {
316 wxChar chNext = m_dir[i + 1];
317 if ( chNext != _T('\\') && chNext != _T('/') )
318 break;
319
320 // ignore the next one, unless it is at the start of a UNC path
321 if (i > 0)
322 i++;
323 else
324 break;
325 }
326 // fall through
327
328 default:
329 // normal char
330 dir += ch;
331 }
332 }
333
334 of.lpstrInitialDir = dir.c_str();
335
336 of.Flags = msw_flags;
337 of.lpfnHook = wxFileDialogHookFunction;
338
339 wxArrayString wildDescriptions, wildFilters;
340
341 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, wildFilters);
342
343 wxASSERT_MSG( items > 0 , _T("empty wildcard list") );
344
345 wxString filterBuffer;
346
347 for (i = 0; i < items ; i++)
348 {
349 filterBuffer += wildDescriptions[i];
350 filterBuffer += wxT("|");
351 filterBuffer += wildFilters[i];
352 filterBuffer += wxT("|");
353 }
354
355 // Replace | with \0
356 for (i = 0; i < filterBuffer.Len(); i++ ) {
357 if ( filterBuffer.GetChar(i) == wxT('|') ) {
358 filterBuffer[i] = wxT('\0');
359 }
360 }
361
362 of.lpstrFilter = (LPTSTR)filterBuffer.c_str();
363 of.nFilterIndex = m_filterIndex + 1;
364
365 //=== Setting defaultFileName >>=========================================
366
367 wxStrncpy( fileNameBuffer, (const wxChar *)m_fileName, wxMAXPATH-1 );
368 fileNameBuffer[ wxMAXPATH-1 ] = wxT('\0');
369
370 of.lpstrFile = fileNameBuffer; // holds returned filename
371 of.nMaxFile = wxMAXPATH;
372
373 // we must set the default extension because otherwise Windows would check
374 // for the existing of a wrong file with wxOVERWRITE_PROMPT (i.e. if the
375 // user types "foo" and the default extension is ".bar" we should force it
376 // to check for "foo.bar" existence and not "foo")
377 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
378 if (m_dialogStyle & wxSAVE)
379 {
380 const wxChar* extension = filterBuffer;
381 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
382
383 for( int i = 0; i < maxFilter; i++ ) // get extension
384 extension = extension + wxStrlen( extension ) + 1;
385
386 // use dummy name a to avoid assert in AppendExtension
387 defextBuffer = AppendExtension(wxT("a"), extension);
388 if (defextBuffer.StartsWith(wxT("a.")))
389 {
390 defextBuffer.Mid(2);
391 of.lpstrDefExt = defextBuffer.c_str();
392 }
393 }
394
395 //== Execute FileDialog >>=================================================
396
397 //== Execute FileDialog >>=================================================
398
399 bool success = (m_dialogStyle & wxSAVE ? GetSaveFileName(&of)
400 : GetOpenFileName(&of)) != 0;
401
402 DWORD errCode = CommDlgExtendedError();
403
404 #ifdef __WIN32__
405 if (!success && (errCode == CDERR_STRUCTSIZE))
406 {
407 // The struct size has changed so try a smaller or bigger size
408
409 int oldStructSize = of.lStructSize;
410 of.lStructSize = oldStructSize - (sizeof(void *) + 2*sizeof(DWORD));
411 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
412 : (GetOpenFileName(&of) != 0);
413 errCode = CommDlgExtendedError();
414
415 if (!success && (errCode == CDERR_STRUCTSIZE))
416 {
417 of.lStructSize = oldStructSize + (sizeof(void *) + 2*sizeof(DWORD));
418 success = (m_dialogStyle & wxSAVE) ? (GetSaveFileName(&of) != 0)
419 : (GetOpenFileName(&of) != 0);
420 }
421 }
422 #endif // __WIN32__
423
424 if ( success )
425 {
426 m_fileNames.Empty();
427
428 if ( ( m_dialogStyle & wxMULTIPLE ) &&
429 #if defined(OFN_EXPLORER)
430 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
431 #else
432 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
433 #endif // OFN_EXPLORER
434 )
435 {
436 #if defined(OFN_EXPLORER)
437 m_dir = fileNameBuffer;
438 i = of.nFileOffset;
439 m_fileName = &fileNameBuffer[i];
440 m_fileNames.Add(m_fileName);
441 i += m_fileName.Len() + 1;
442
443 while (fileNameBuffer[i] != wxT('\0'))
444 {
445 m_fileNames.Add(&fileNameBuffer[i]);
446 i += wxStrlen(&fileNameBuffer[i]) + 1;
447 }
448 #else
449 wxStringTokenizer toke(fileNameBuffer, _T(" \t\r\n"));
450 m_dir = toke.GetNextToken();
451 m_fileName = toke.GetNextToken();
452 m_fileNames.Add(m_fileName);
453
454 while (toke.HasMoreTokens())
455 m_fileNames.Add(toke.GetNextToken());
456 #endif // OFN_EXPLORER
457
458 wxString dir(m_dir);
459 if ( m_dir.Last() != _T('\\') )
460 dir += _T('\\');
461
462 m_path = dir + m_fileName;
463 m_filterIndex = (int)of.nFilterIndex - 1;
464 }
465 else
466 {
467 //=== Adding the correct extension >>=================================
468
469 m_filterIndex = (int)of.nFilterIndex - 1;
470
471 if ( !of.nFileExtension ||
472 (of.nFileExtension && fileNameBuffer[of.nFileExtension] == wxT('\0')) )
473 {
474 // User has typed a filename without an extension:
475 const wxChar* extension = filterBuffer;
476 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
477
478 for( int i = 0; i < maxFilter; i++ ) // get extension
479 extension = extension + wxStrlen( extension ) + 1;
480
481 m_fileName = AppendExtension(fileNameBuffer, extension);
482 wxStrncpy(fileNameBuffer, m_fileName.c_str(), wxMin(m_fileName.Len(), wxMAXPATH-1));
483 fileNameBuffer[wxMin(m_fileName.Len(), wxMAXPATH-1)] = wxT('\0');
484 }
485
486 m_path = fileNameBuffer;
487 m_fileName = wxFileNameFromPath(fileNameBuffer);
488 m_fileNames.Add(m_fileName);
489 m_dir = wxPathOnly(fileNameBuffer);
490 }
491 }
492 else
493 {
494 // common dialog failed - why?
495 #ifdef __WXDEBUG__
496 DWORD dwErr = CommDlgExtendedError();
497 if ( dwErr != 0 )
498 {
499 // this msg is only for developers
500 wxLogError(wxT("Common dialog failed with error code %0lx."),
501 dwErr);
502 }
503 //else: it was just cancelled
504 #endif
505 }
506
507 return success ? wxID_OK : wxID_CANCEL;
508
509 }
510
511 #endif // wxUSE_FILEDLG
512