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