Audacity 3.2.0
win/FileDialogPrivate.cpp
Go to the documentation of this file.
1//
2// Copied from wxWidgets 3.0.2 and modified for Audacity
3//
5// Name: src/msw/filedlg.cpp
6// Purpose: wxFileDialog
7// Author: Julian Smart
8// Modified by: Leland Lucius
9// Created: 01/02/97
10// Copyright: (c) Julian Smart
11// Licence: wxWindows licence
13
14// ============================================================================
15// declarations
16// ============================================================================
17
18// ----------------------------------------------------------------------------
19// headers
20// ----------------------------------------------------------------------------
21
22// For compilers that support precompilation, includes "wx.h".
23#include "wx/wxprec.h"
24
25#ifdef __BORLANDC__
26#pragma hdrstop
27#endif
28
29#ifndef WX_PRECOMP
30 #include <wx/msw/wrapcdlg.h>
31 #include <wx/msw/missing.h>
32 #include <wx/utils.h>
33 #include <wx/msgdlg.h>
34 #include <wx/filefn.h>
35 #include <wx/intl.h>
36 #include <wx/log.h>
37 #include <wx/app.h>
38 #include <wx/math.h>
39#endif
40
41#include <stdlib.h>
42#include <string.h>
43
44#include <wx/dynlib.h>
45#include <wx/filename.h>
46#include <wx/scopeguard.h>
47#include <wx/sizer.h>
48#include <wx/tokenzr.h>
49#include <wx/modalhook.h>
50#include <wx/filectrl.h>
51
52#include "../FileDialog.h"
53
54#include <shlobj.h>
55
56// ----------------------------------------------------------------------------
57// constants
58// ----------------------------------------------------------------------------
59
60#ifdef _WIN32
61# define wxMAXPATH 65534
62#else
63# define wxMAXPATH 1024
64#endif
65
66# define wxMAXFILE 1024
67
68# define wxMAXEXT 5
69
70// ----------------------------------------------------------------------------
71// globals
72// ----------------------------------------------------------------------------
73
74// standard dialog size
75static wxRect gs_rectDialog(0, 0, 428, 266);
76
77// ============================================================================
78// implementation
79// ============================================================================
80
81IMPLEMENT_CLASS(FileDialog, wxFileDialogBase)
82
83// ----------------------------------------------------------------------------
84
85namespace
86{
87
88#if wxUSE_DYNLIB_CLASS
89
90typedef BOOL (WINAPI *GetProcessUserModeExceptionPolicy_t)(LPDWORD);
91typedef BOOL (WINAPI *SetProcessUserModeExceptionPolicy_t)(DWORD);
92
93GetProcessUserModeExceptionPolicy_t gs_pfnGetProcessUserModeExceptionPolicy
94 = (GetProcessUserModeExceptionPolicy_t) -1;
95
96SetProcessUserModeExceptionPolicy_t gs_pfnSetProcessUserModeExceptionPolicy
97 = (SetProcessUserModeExceptionPolicy_t) -1;
98
99DWORD gs_oldExceptionPolicyFlags = 0;
100
101bool gs_changedPolicy = false;
102
103#endif // #if wxUSE_DYNLIB_CLASS
104
105/*
106Since Windows 7 by default (callback) exceptions aren't swallowed anymore
107with native x64 applications. Exceptions can occur in a file dialog when
108using the hook procedure in combination with third-party utilities.
109Since Windows 7 SP1 the swallowing of exceptions can be enabled again
110by using SetProcessUserModeExceptionPolicy.
111*/
113{
114#if wxUSE_DYNLIB_CLASS
115 gs_changedPolicy = false;
116
117 wxLoadedDLL dllKernel32(wxT("kernel32.dll"));
118
119 if ( gs_pfnGetProcessUserModeExceptionPolicy
120 == (GetProcessUserModeExceptionPolicy_t) -1)
121 {
122 wxDL_INIT_FUNC(gs_pfn, GetProcessUserModeExceptionPolicy, dllKernel32);
123 wxDL_INIT_FUNC(gs_pfn, SetProcessUserModeExceptionPolicy, dllKernel32);
124 }
125
126 if ( !gs_pfnGetProcessUserModeExceptionPolicy
127 || !gs_pfnSetProcessUserModeExceptionPolicy
128 || !gs_pfnGetProcessUserModeExceptionPolicy(&gs_oldExceptionPolicyFlags) )
129 {
130 return;
131 }
132
133 if ( gs_pfnSetProcessUserModeExceptionPolicy(gs_oldExceptionPolicyFlags
134 | 0x1 /* PROCESS_CALLBACK_FILTER_ENABLED */ ) )
135 {
136 gs_changedPolicy = true;
137 }
138
139#endif // wxUSE_DYNLIB_CLASS
140}
141
143{
144#if wxUSE_DYNLIB_CLASS
145 if (gs_changedPolicy)
146 {
147 gs_changedPolicy = false;
148 (void) gs_pfnSetProcessUserModeExceptionPolicy(gs_oldExceptionPolicyFlags);
149 }
150#endif // wxUSE_DYNLIB_CLASS
151}
152
153} // unnamed namespace
154
155// ----------------------------------------------------------------------------
156// hook function for moving the dialog
157// ----------------------------------------------------------------------------
158
159UINT_PTR APIENTRY FileDialog::ParentHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
160{
161 OPENFILENAME *pOfn = reinterpret_cast<OPENFILENAME *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
162 return reinterpret_cast<FileDialog *>(pOfn->lCustData)->MSWParentHook(hDlg, iMsg, wParam, lParam, pOfn);
163}
164
165UINT_PTR FileDialog::MSWParentHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam, OPENFILENAME *pOfn)
166{
167 // Allow default handling to process first
168 UINT_PTR ret = CallWindowProc(mParentProc, hDlg, iMsg, wParam, lParam);
169
170 if (iMsg == WM_SIZE)
171 {
172 MSWOnSize(mParentDlg, pOfn);
173 }
174
175 if (iMsg == WM_GETMINMAXINFO)
176 {
177 MSWOnGetMinMaxInfo(mParentDlg, pOfn, reinterpret_cast<LPMINMAXINFO>(lParam));
178 }
179
180 return ret;
181}
182
183void FileDialog::MSWOnSize(HWND hDlg, LPOPENFILENAME pOfn)
184{
185 wxRect r;
186 wxCopyRECTToRect(wxGetClientRect(hDlg), r);
187
188 SetHWND(mChildDlg);
189
190 SetWindowPos(mChildDlg,
191 HWND_TOP,
192 0,
193 0,
194 r.GetWidth(),
195 r.GetHeight(),
196 SWP_NOZORDER | SWP_NOMOVE);
197
198 SetSize(r);
199
200 if (mRoot)
201 {
202 mRoot->SetSize(r.GetWidth(), mRoot->GetSize().GetHeight());
203 }
204
205 SetHWND(NULL);
206}
207
208// Provide the minimum size of the dialog
209//
210// We've captured the full dialog size in MSWOnInitDone() below. This will be returned
211// as the minimum size.
212//
213// When the user tries to resize the dialog, for some unknown reason the common dialog control
214// doesn't let the user resize it smaller than it was the last time the dialog was used. This
215// may be a problem in this code and/or may only be a concern under Windows 10. Either way, we
216// override the minimum size supplied by the common dialog control with our own size here.
217void FileDialog::MSWOnGetMinMaxInfo(HWND hwnd, LPOPENFILENAME pOfn, LPMINMAXINFO pMmi)
218{
219 if (mMinSize.x > 0 && mMinSize.y > 0)
220 {
221 pMmi->ptMinTrackSize = mMinSize;
222 }
223}
224
225UINT_PTR APIENTRY FileDialog::DialogHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
226{
227 OPENFILENAME *pOfn;
228
229 if (iMsg == WM_INITDIALOG)
230 {
231 pOfn = reinterpret_cast<OPENFILENAME *>(lParam);
232 }
233 else
234 {
235 pOfn = reinterpret_cast<OPENFILENAME *>(GetWindowLongPtr(hDlg, GWLP_USERDATA));
236 }
237
238 return reinterpret_cast<FileDialog *>(pOfn->lCustData)->MSWDialogHook(hDlg, iMsg, wParam, lParam, pOfn);
239}
240
241UINT_PTR FileDialog::MSWDialogHook(HWND hDlg, UINT iMsg, WPARAM WXUNUSED(wParam), LPARAM lParam, OPENFILENAME *pOfn)
242{
243 switch (iMsg)
244 {
245 case WM_INITDIALOG:
246 MSWOnInitDialog(hDlg, pOfn);
247 break;
248
249 case WM_DESTROY:
250 MSWOnDestroy(hDlg, pOfn);
251 break;
252
253 case WM_NOTIFY:
254 {
255 NMHDR * const pNM = reinterpret_cast<NMHDR *>(lParam);
256 if (pNM->code > CDN_LAST && pNM->code <= CDN_FIRST)
257 {
258 OFNOTIFY * const pNotifyCode = reinterpret_cast<OFNOTIFY *>(lParam);
259 switch (pNotifyCode->hdr.code)
260 {
261 case CDN_INITDONE:
262 MSWOnInitDone(hDlg, pOfn);
263 break;
264
265 case CDN_FOLDERCHANGE:
266 MSWOnFolderChange(hDlg, pOfn);
267 break;
268
269 case CDN_SELCHANGE:
270 MSWOnSelChange(hDlg, pOfn);
271 break;
272
273 case CDN_TYPECHANGE:
274 MSWOnTypeChange(hDlg, pOfn);
275 break;
276 }
277 }
278 }
279 break;
280 }
281
282 // do the default processing
283 return 0;
284}
285
286void FileDialog::MSWOnInitDialog(HWND hDlg, LPOPENFILENAME pOfn)
287{
288 // Since we've specified the OFN_EXPLORER flag, the "real" dialog is the parent of this one
289 mParentDlg = ::GetParent(hDlg);
290
291 // Now we can initialize the disabler
293
294 // This is the dialog were our controls will go
295 mChildDlg = hDlg;
296
297 // Store the OPENFILENAME pointer in each window
298 SetWindowLongPtr(mParentDlg, GWLP_USERDATA, reinterpret_cast<LPARAM>(pOfn));
299 SetWindowLongPtr(mChildDlg, GWLP_USERDATA, reinterpret_cast<LPARAM>(pOfn));
300
301 // Subclass the parent dialog so we can receive WM_SIZE messages
302 mParentProc = reinterpret_cast<WNDPROC>(SetWindowLongPtr(mParentDlg, GWLP_WNDPROC, reinterpret_cast<LPARAM>(&ParentHook)));
303
304 // set HWND for wx
305 SetHWND(mChildDlg);
306
307 if (HasUserPaneCreator())
308 {
309 // Create the root window
310 wxBoxSizer *verticalSizer = new wxBoxSizer(wxVERTICAL);
311 mRoot = new wxPanel(this, wxID_ANY);
312
313 wxPanel *userpane = new wxPanel(mRoot, wxID_ANY);
314 CreateUserPane(userpane);
315
316 wxBoxSizer *horizontalSizer = new wxBoxSizer(wxHORIZONTAL);
317 horizontalSizer->Add(userpane, 1, wxEXPAND);
318 verticalSizer->Add(horizontalSizer, 1, wxEXPAND);
319
320 mRoot->SetSizer(verticalSizer);
321 mRoot->Layout();
322 mRoot->Fit();
323
324 // This reserves space for the additional panel
325 wxSize sz = mRoot->GetBestSize();
326 SetWindowPos(mChildDlg,
327 HWND_TOP,
328 0,
329 0,
330 sz.GetWidth(),
331 sz.GetHeight(),
332 SWP_NOZORDER | SWP_NOMOVE);
333
334 }
335
336 SetHWND(NULL);
337}
338
339void FileDialog::MSWOnDestroy(HWND hDlg, LPOPENFILENAME WXUNUSED(pOfn))
340{
341 // Save final dialog position for next time
342 wxCopyRECTToRect(wxGetWindowRect(mParentDlg), gs_rectDialog);
343
344 // Must explicitly delete the root window. Otherwise, wx will try to
345 // destroy it when the FileDialog is deleted. But, the windows will
346 // have already been deleted as a result of the OpenFile dialog being
347 // destroyed.
348 delete mRoot;
349}
350
351void FileDialog::MSWOnInitDone(HWND hDlg, LPOPENFILENAME pOfn)
352{
353 // set HWND so that our DoMoveWindow() works correctly
354 SetHWND(mChildDlg);
355
356 // capture the full initial size of the dialog to use as the minimum size
357 RECT r;
358 GetWindowRect(mParentDlg, &r);
359 mMinSize = {r.right - r.left, r.bottom - r.top};
360
361 if (m_centreDir)
362 {
363 // now we have the real dialog size, remember it
364 RECT rect;
365 GetWindowRect(mParentDlg, &rect);
366 gs_rectDialog = wxRectFromRECT(rect);
367
368 // and position the window correctly: notice that we must use the base
369 // class version as our own doesn't do anything except setting flags
370 wxFileDialogBase::DoCentre(m_centreDir);
371 }
372 else // need to just move it to the correct place
373 {
374 SetPosition(gs_rectDialog.GetPosition());
375 }
376
377 // Filter change event must be sent once initialized
378 MSWOnTypeChange(hDlg, pOfn);
379
380 // we shouldn't destroy this HWND
381 SetHWND(NULL);
382}
383
384void FileDialog::MSWOnFolderChange(HWND hDlg, LPOPENFILENAME pOfn)
385{
386 FilterFiles(mParentDlg, true);
387
388 wxChar path[wxMAXPATH];
389 int result = CommDlg_OpenSave_GetFolderPath(::GetParent(hDlg), path, WXSIZEOF(path));
390 if (result < 0 || result > WXSIZEOF(path))
391 {
392 return;
393 }
394
395 m_dir = path;
396
397 wxFileCtrlEvent event(wxEVT_FILECTRL_FOLDERCHANGED, this, GetId());
398 event.SetDirectory(m_dir);
399 GetEventHandler()->ProcessEvent(event);
400}
401
402void FileDialog::MSWOnSelChange(HWND hDlg, LPOPENFILENAME pOfn)
403{
404 // set HWND for wx
405 SetHWND(mChildDlg);
406
407 // Get pointer to the ListView control
408 HWND lv = ::GetDlgItem(::GetDlgItem(mParentDlg, lst2), 1);
409 if (lv == NULL)
410 {
411 return;
412 }
413
414 wxChar path[wxMAXPATH];
415 int result = CommDlg_OpenSave_GetFilePath(::GetParent(hDlg), path, WXSIZEOF(path));
416 if (result < 0 || result > WXSIZEOF(path))
417 {
418 return;
419 }
420
421 m_path = path;
422 m_fileName = wxFileNameFromPath(m_path);
423 m_dir = wxPathOnly(m_path);
424
425 m_fileNames.Clear();
426 m_fileNames.Add(m_fileName);
427
428 wxFileCtrlEvent event(wxEVT_FILECTRL_SELECTIONCHANGED, this, GetId());
429 event.SetDirectory(m_dir);
430 event.SetFiles(m_fileNames);
431 GetEventHandler()->ProcessEvent(event);
432
433 // we shouldn't destroy this HWND
434 SetHWND(NULL);
435}
436
437void FileDialog::MSWOnTypeChange(HWND hDlg, LPOPENFILENAME pOfn)
438{
439 // set HWND for wx
440 SetHWND(mChildDlg);
441
442 ParseFilter(pOfn->nFilterIndex);
443 FilterFiles(mParentDlg, true);
444
445 m_filterIndex = pOfn->nFilterIndex - 1;
446
447 wxFileCtrlEvent event(wxEVT_FILECTRL_FILTERCHANGED, this, GetId());
448 event.SetFilterIndex(m_filterIndex);
449 GetEventHandler()->ProcessEvent(event);
450
451 // we shouldn't destroy this HWND
452 SetHWND(NULL);
453}
454
455#define WM_GETISHELLBROWSER WM_USER + 7
456
457void FileDialog::FilterFiles(HWND hwnd, bool refresh)
458{
459 IShellFolder *ishell = NULL;
460 IShellBrowser *ishellbrowser = NULL; // Does not have to be released
461 IShellView *ishellview = NULL;
462 IFolderView *ifolderview = NULL;
463 LPMALLOC imalloc = NULL;
464 HRESULT hr;
465
466 // Get pointer to the ListView control
467 HWND lv = ::GetDlgItem(::GetDlgItem(hwnd, lst2), 1);
468 if (lv == NULL)
469 {
470 return;
471 }
472
473 // Get shell's memory allocation interface (must be Release()'d)
474 hr = SHGetMalloc(&imalloc);
475 if ((hr != NOERROR) || (imalloc == NULL))
476 {
477 wxASSERT((hr == NOERROR) && (imalloc != NULL));
478 return;
479 }
480
481 // Get IShellBrowser interface for current dialog
482 ishellbrowser = (IShellBrowser*)::SendMessage(hwnd, WM_GETISHELLBROWSER, 0, 0);
483 if (ishellbrowser)
484 {
485 // Get IShellBrowser interface for returned browser
486 if (ishellbrowser->QueryActiveShellView(&ishellview) == S_OK)
487 {
488 // Get the IFolderView interface...available on XP or greater
489 ishellview->QueryInterface(IID_IFolderView, (void **)&ifolderview);
490 }
491 }
492
493 // Init
494 LVITEM lvi;
495 wxZeroMemory(lvi);
496
497 // Process all items
498 int fltcnt = (int) m_Filters.GetCount();
499 int itmcnt = ListView_GetItemCount(lv);
500 for (int itm = 0; itm < itmcnt; itm++)
501 {
502 // Retrieve the file IDL
503 lvi.iItem = itm;
504 lvi.mask = LVIF_PARAM;
505 if (ListView_GetItem(lv, &lvi) != TRUE)
506 {
507 wxASSERT(FALSE);
508 break;
509 }
510
511 LPCITEMIDLIST fidl = (LPCITEMIDLIST) lvi.lParam;
512
513 // On Vista, lParam no longer contains the pidl so retrieve it via the
514 // IFolderView interface. This interface is only available on XP or higher
515 // so if that limitation isn't workable, use IShellView::GetItemObject() to
516 // retrieve items.
517 if (fidl == NULL && ifolderview != NULL)
518 {
519 ifolderview->Item(itm, (LPITEMIDLIST *) &fidl);
520 }
521
522 if (fidl == NULL)
523 {
524 wxASSERT(fidl != NULL);
525 break;
526 }
527
528 // Retrieve the IShellFolder interface of the parent (must be Release()'d)
529 if (ishell == NULL)
530 {
531 hr = SHBindToParent(fidl, IID_IShellFolder, (void **)&ishell, NULL);
532 if (!SUCCEEDED(hr))
533 {
534 wxASSERT(SUCCEEDED(hr));
535 break;
536 }
537 }
538
539 // Get the attributes of the object
540 DWORD attr = SFGAO_FOLDER | SFGAO_BROWSABLE;
541 hr = ishell->GetAttributesOf(1, &fidl, &attr);
542 if (!SUCCEEDED(hr))
543 {
544 wxASSERT(SUCCEEDED(hr));
545 break;
546 }
547
548 // Allow all folders (things like zip files get filtered below)
549 if ((attr & (SFGAO_FOLDER)) && !(attr & SFGAO_BROWSABLE))
550 {
551 continue;
552 }
553
554 // Retrieve the parsable name of the object (includes extension)
555 STRRET str;
556 hr = ishell->GetDisplayNameOf(fidl, SHGDN_INFOLDER | SHGDN_FORPARSING, &str);
557 if (hr != NOERROR)
558 {
559 // For some objects, we get back an error of 80070057. I'm assuming this
560 // means there is no way to represent the name (like some sort of virtual name)
561 // or I've not used the correct PIDL. But, in either case, it "probably"
562 // represents some sort of folder (at least in all cases I've seen), so we
563 // simply allow it to display.
564 continue;
565 }
566
567 // Convert result to wxString
568 wxString filename;
569 switch (str.uType)
570 {
571 case STRRET_WSTR:
572 filename = str.pOleStr;
573 imalloc->Free(str.pOleStr);
574 break;
575
576 case STRRET_OFFSET:
577 filename = wxString(((char *)fidl) + str.uOffset, wxConvISO8859_1);
578 break;
579
580 case STRRET_CSTR:
581 filename = wxString(str.cStr, wxConvISO8859_1);
582 break;
583 }
584
585 // Convert the filename to lowercase (and remember to write filters in lowercase!)
586 filename = filename.Lower();
587
588 // Attempt to match it to all of our filters
589 bool match = false;
590 for (int flt = 0; flt < fltcnt; flt++)
591 {
592 if (wxMatchWild(m_Filters[flt], filename, false))
593 {
594 match = true;
595 break;
596 }
597 }
598
599 // Remove it from the display if it didn't match any of the filters.
600 if (!match)
601 {
602 ListView_DeleteItem(lv, itm);
603 itm--;
604 itmcnt--;
605 }
606 }
607
608 // On Vista and maybe XP, we seem to need to refresh the view after
609 // changing the filters. But, only refresh for type changes and not
610 // selection changes since it causes additional selection change
611 // events to occur.
612 if (ishellview && refresh)
613 {
614 ishellview->Refresh();
615 }
616
617 // Release the interface
618 if (ifolderview)
619 {
620 ifolderview->Release();
621 }
622
623 // Release the interface
624 if (ishellview)
625 {
626 ishellview->Release();
627 }
628
629 // Release the interface
630 if (ishell)
631 {
632 ishell->Release();
633 }
634
635 // Release the interface
636 if (imalloc)
637 {
638 imalloc->Release();
639 }
640}
641
643{
644 m_Filters.Empty();
645
646 wxStringTokenizer tokenWild(m_FilterGroups[index - 1], wxT(";"));
647
648 while (tokenWild.HasMoreTokens())
649 {
650 wxString token = tokenWild.GetNextToken();
651 if (m_Filters.Index(token, false) == wxNOT_FOUND)
652 {
653 m_Filters.Add(token);
654 }
655 }
656}
657
658// ----------------------------------------------------------------------------
659// FileDialog
660// ----------------------------------------------------------------------------
661
664{
665 Init();
666}
667
668FileDialog::FileDialog(wxWindow *parent,
669 const wxString& message,
670 const wxString& defaultDir,
671 const wxString& defaultFile,
672 const wxString& wildCard,
673 long style,
674 const wxPoint& pos,
675 const wxSize& sz,
676 const wxString& name)
678{
679 Init();
680
681 FileDialogBase::Create(parent,message,defaultDir,defaultFile,wildCard,style,pos,sz,name);
682}
683
685{
686 mRoot = NULL;
687 mMinSize = {0, 0};
688
689 // NB: all style checks are done by wxFileDialogBase::Create
690
691 m_bMovedWindow = false;
692 m_centreDir = 0;
693
694 // Must set to zero, otherwise the wx routines won't size the window
695 // the second time you call the file dialog, because it thinks it is
696 // already at the requested size.. (when centering)
697 gs_rectDialog.x =
698 gs_rectDialog.y = 0;
699}
700
701void FileDialog::GetPaths(wxArrayString& paths) const
702{
703 paths.Empty();
704
705 wxString dir(m_dir);
706 if (m_dir.empty() || m_dir.Last() != wxT('\\'))
707 dir += wxT('\\');
708
709 size_t count = m_fileNames.GetCount();
710 for (size_t n = 0; n < count; n++)
711 {
712 if (wxFileName(m_fileNames[n]).IsAbsolute())
713 paths.Add(m_fileNames[n]);
714 else
715 paths.Add(dir + m_fileNames[n]);
716 }
717}
718
719void FileDialog::GetFilenames(wxArrayString& files) const
720{
721 files = m_fileNames;
722}
723
724void FileDialog::SetFileExtension(const wxString& extension)
725{
726 if (mParentDlg)
727 {
728 wxChar path[wxMAXPATH];
729
730 if (CommDlg_OpenSave_GetFilePath(mParentDlg, path, WXSIZEOF(path)))
731 {
732 wxFileName fn(path);
733 fn.SetExt(extension);
734
735 // Change the currently entered file name.
736 CommDlg_OpenSave_SetControlText(mParentDlg, edt1, fn.GetFullName().t_str());
737
738 // Make this the default extension as well. So if the user specifies a file
739 // name without an extension, this one will be used instead of the first
740 // extension in the filter list.
741 CommDlg_OpenSave_SetDefExt(mParentDlg, fn.GetExt().t_str());
742 }
743 }
744}
745
746void FileDialog::DoGetPosition( int *x, int *y ) const
747{
748 if (x)
749 *x = gs_rectDialog.x;
750 if (y)
751 *y = gs_rectDialog.y;
752}
753
754void FileDialog::DoGetSize(int *width, int *height) const
755{
756 if (width)
757 *width = gs_rectDialog.width;
758 if (height)
759 *height = gs_rectDialog.height;
760}
761
762void FileDialog::DoMoveWindow(int x, int y, int WXUNUSED(width), int WXUNUSED(height))
763{
764 m_bMovedWindow = true;
765
766 gs_rectDialog.x = x;
767 gs_rectDialog.y = y;
768
769 // our HWND is only set when we're called from MSWOnInitDone(), test if
770 // this is the case
771 HWND hwnd = GetHwnd();
772 if (hwnd)
773 {
774 // size of the dialog can't be changed because the controls are not
775 // laid out correctly then
776 ::SetWindowPos(hwnd, HWND_TOP, x, y, 0, 0, SWP_NOZORDER | SWP_NOSIZE);
777 }
778 else // just remember that we were requested to move the window
779 {
780 m_bMovedWindow = true;
781
782 // if Centre() had been called before, it shouldn't be taken into
783 // account now
784 m_centreDir = 0;
785 }
786}
787
789{
790 m_centreDir = dir;
791 m_bMovedWindow = true;
792
793 // it's unnecessary to do anything else at this stage as we'll redo it in
794 // MSWOnInitDone() anyhow
795}
796
797// helper used below in ShowCommFileDialog(): style is used to determine
798// whether to show the "Save file" dialog (if it contains wxFD_SAVE bit) or
799// "Open file" one; returns true on success or false on failure in which case
800// err is filled with the CDERR_XXX constant
801static bool DoShowCommFileDialog(OPENFILENAME *of, long style, DWORD *err)
802{
803 if (style & wxFD_SAVE ? GetSaveFileName(of) : GetOpenFileName(of))
804 return true;
805
806 if (err)
807 {
808 *err = CommDlgExtendedError();
809 }
810
811 return false;
812}
813
814// We want to use OPENFILENAME struct version 5 (Windows 2000/XP) but we don't
815// know if the OPENFILENAME declared in the currently used headers is a V5 or
816// V4 (smaller) one so we try to manually extend the struct in case it is the
817// old one.
818//
819// We don't do this on Windows CE nor under Win64, however, as there are no
820// compilers with old headers for these architectures
821#if defined(__WXWINCE__) || defined(__WIN64__)
822typedef OPENFILENAME wxOPENFILENAME;
823
824static const DWORD gs_ofStructSize = sizeof(OPENFILENAME);
825#else // !__WXWINCE__ || __WIN64__
826#define wxTRY_SMALLER_OPENFILENAME
827
828struct wxOPENFILENAME : public OPENFILENAME
829{
830 // fields added in Windows 2000/XP comdlg32.dll version
831 void *pVoid;
832 DWORD dw1;
833 DWORD dw2;
834};
835
836// hardcoded sizeof(OPENFILENAME) in the Platform SDK: we have to do it
837// because sizeof(OPENFILENAME) in the headers we use when compiling the
838// library could be less if _WIN32_WINNT is not >= 0x500
839static const DWORD wxOPENFILENAME_V5_SIZE = 88;
840
841// this is hardcoded sizeof(OPENFILENAME_NT4) from Platform SDK
842static const DWORD wxOPENFILENAME_V4_SIZE = 76;
843
844// always try the new one first
846#endif // __WXWINCE__ || __WIN64__/!...
847
848static bool ShowCommFileDialog(OPENFILENAME *of, long style)
849{
850 DWORD errCode;
851 bool success = DoShowCommFileDialog(of, style, &errCode);
852
853#ifdef wxTRY_SMALLER_OPENFILENAME
854 // the system might be too old to support the new version file dialog
855 // boxes, try with the old size
856 if (!success && errCode == CDERR_STRUCTSIZE &&
857 of->lStructSize != wxOPENFILENAME_V4_SIZE)
858 {
859 of->lStructSize = wxOPENFILENAME_V4_SIZE;
860
861 success = DoShowCommFileDialog(of, style, &errCode);
862
863 if (success || !errCode)
864 {
865 // use this struct size for subsequent dialogs
866 gs_ofStructSize = of->lStructSize;
867 }
868 }
869#endif // wxTRY_SMALLER_OPENFILENAME
870
871 if (!success && errCode == FNERR_INVALIDFILENAME && of->lpstrFile[0])
872 {
873 // this can happen if the default file name is invalid, try without it
874 // now
875 of->lpstrFile[0] = wxT('\0');
876 success = DoShowCommFileDialog(of, style, &errCode);
877 }
878
879 if (!success)
880 {
881 // common dialog failed - why?
882 if (errCode != 0)
883 {
884 wxLogError(wxT("File dialog failed with error code %0lx."), errCode);
885 }
886 //else: it was just cancelled
887
888 return false;
889 }
890
891 return true;
892}
893
895{
896 WX_HOOK_MODAL_DIALOG();
897
898 HWND hWnd = 0;
899 if (m_parent) hWnd = (HWND) m_parent->GetHWND();
900 if (!hWnd && wxTheApp->GetTopWindow())
901 hWnd = (HWND) wxTheApp->GetTopWindow()->GetHWND();
902
903 static wxChar fileNameBuffer [ wxMAXPATH ]; // the file-name
904 wxChar titleBuffer [ wxMAXFILE+1+wxMAXEXT ]; // the file-name, without path
905
906 *fileNameBuffer = wxT('\0');
907 *titleBuffer = wxT('\0');
908
909 // We always need EXPLORER and ENABLEHOOK to use our filtering code
910 DWORD msw_flags = OFN_HIDEREADONLY | OFN_EXPLORER | OFN_ENABLEHOOK | OFN_ENABLESIZING | OFN_ENABLETEMPLATEHANDLE;
911
912 if (HasFdFlag(wxFD_FILE_MUST_EXIST))
913 msw_flags |= OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
914
915 /*
916 If the window has been moved the programmer is probably
917 trying to center or position it. Thus we set the callback
918 or hook function so that we can actually adjust the position.
919 Without moving or centering the dlg, it will just stay
920 in the upper left of the frame, it does not center
921 automatically.
922 */
923 if (m_bMovedWindow || HasExtraControlCreator()) // we need these flags.
924 {
926 msw_flags |= OFN_EXPLORER|OFN_ENABLEHOOK;
927#ifndef __WXWINCE__
928 msw_flags |= OFN_ENABLESIZING;
929#endif
930 }
931
932 wxON_BLOCK_EXIT0(RestoreExceptionPolicy);
933
934 if (HasFdFlag(wxFD_MULTIPLE))
935 {
936 // OFN_EXPLORER must always be specified with OFN_ALLOWMULTISELECT
937 msw_flags |= OFN_EXPLORER | OFN_ALLOWMULTISELECT;
938 }
939
940 // if wxCHANGE_DIR flag is not given we shouldn't change the CWD which the
941 // standard dialog does by default (notice that under NT it does it anyhow,
942 // OFN_NOCHANGEDIR or not, see below)
943 if (!HasFdFlag(wxFD_CHANGE_DIR))
944 {
945 msw_flags |= OFN_NOCHANGEDIR;
946 }
947
948 if (HasFdFlag(wxFD_OVERWRITE_PROMPT))
949 {
950 msw_flags |= OFN_OVERWRITEPROMPT;
951 }
952
953 // Define a dummy dialog box template
954 GlobalPtr hgbl(256, GMEM_ZEROINIT);
955 GlobalPtrLock hgblLock(hgbl);
956 LPDLGTEMPLATE lpdt = static_cast<LPDLGTEMPLATE>((void *)hgblLock);
957 lpdt->style = DS_CONTROL | WS_CHILD | WS_CLIPSIBLINGS;
958 lpdt->cdit = 0; // Number of controls
959 lpdt->x = 0;
960 lpdt->y = 0;
961 lpdt->cx = 0;
962 lpdt->cy = 0;
963
964 OPENFILENAME of;
965 wxZeroMemory(of);
966
967 // Allow Places bar to show on supported platforms
968 of.lStructSize = sizeof(OPENFILENAME);
969 of.hwndOwner = hWnd;
970 of.lpstrTitle = m_message.t_str();
971 of.lpstrFileTitle = titleBuffer;
972 of.nMaxFileTitle = wxMAXFILE + 1 + wxMAXEXT;
973 of.hInstance = (HINSTANCE) lpdt;
974
975 // Convert forward slashes to backslashes (file selector doesn't like
976 // forward slashes) and also squeeze multiple consecutive slashes into one
977 // as it doesn't like two backslashes in a row neither
978
979 wxString dir;
980 size_t i, len = m_dir.length();
981 dir.reserve(len);
982 for ( i = 0; i < len; i++ )
983 {
984 wxChar ch = m_dir[i];
985 switch ( ch )
986 {
987 case wxT('/'):
988 // convert to backslash
989 ch = wxT('\\');
990
991 // fall through
992
993 case wxT('\\'):
994 while ( i < len - 1 )
995 {
996 wxChar chNext = m_dir[i + 1];
997 if ( chNext != wxT('\\') && chNext != wxT('/') )
998 break;
999
1000 // ignore the next one, unless it is at the start of a UNC path
1001 if (i > 0)
1002 i++;
1003 else
1004 break;
1005 }
1006 // fall through
1007
1008 default:
1009 // normal char
1010 dir += ch;
1011 }
1012 }
1013
1014 of.lpstrInitialDir = dir.c_str();
1015
1016 of.Flags = msw_flags;
1017 of.lpfnHook = DialogHook;
1018 of.lCustData = (LPARAM) this;
1019
1020 wxArrayString wildDescriptions;
1021
1022 size_t items = wxParseCommonDialogsFilter(m_wildCard, wildDescriptions, m_FilterGroups);
1023
1024 wxASSERT_MSG( items > 0 , wxT("empty wildcard list") );
1025
1026 wxString filterBuffer;
1027
1028 for (i = 0; i < items ; i++)
1029 {
1030 filterBuffer += wildDescriptions[i];
1031 filterBuffer += wxT("|");
1032 filterBuffer += m_FilterGroups[i];
1033 filterBuffer += wxT("|");
1034 }
1035
1036 // Replace | with \0
1037 for (i = 0; i < filterBuffer.Len(); i++ )
1038 {
1039 if ( filterBuffer.GetChar(i) == wxT('|') )
1040 {
1041 filterBuffer[i] = wxT('\0');
1042 }
1043 }
1044
1045 of.lpstrFilter = (LPCTSTR)filterBuffer.c_str();
1046 of.nFilterIndex = m_filterIndex + 1;
1047
1048 ParseFilter(of.nFilterIndex);
1049
1050 //=== Setting defaultFileName >>=========================================
1051
1052 wxStrlcpy(fileNameBuffer, m_fileName.c_str(), WXSIZEOF(fileNameBuffer));
1053
1054 of.lpstrFile = fileNameBuffer; // holds returned filename
1055 of.nMaxFile = wxMAXPATH;
1056
1057 // we must set the default extension because otherwise Windows would check
1058 // for the existing of a wrong file with wxOVERWRITE_PROMPT (i.e. if the
1059 // user types "foo" and the default extension is ".bar" we should force it
1060 // to check for "foo.bar" existence and not "foo")
1061 wxString defextBuffer; // we need it to be alive until GetSaveFileName()!
1062 if (HasFdFlag(wxFD_SAVE))
1063 {
1064 const wxChar* extension = filterBuffer;
1065 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
1066
1067 for( int i = 0; i < maxFilter; i++ ) // get extension
1068 extension = extension + wxStrlen( extension ) + 1;
1069
1070 // use dummy name a to avoid assert in AppendExtension
1071 defextBuffer = AppendExtension(wxT("a"), extension);
1072 if (defextBuffer.StartsWith(wxT("a.")))
1073 {
1074 defextBuffer = defextBuffer.Mid(2); // remove "a."
1075 of.lpstrDefExt = defextBuffer.c_str();
1076 }
1077 }
1078
1079 // store off before the standard windows dialog can possibly change it
1080 const wxString cwdOrig = wxGetCwd();
1081
1082 //== Execute FileDialog >>=================================================
1083
1084 if (!ShowCommFileDialog(&of, m_windowStyle))
1085 return wxID_CANCEL;
1086
1087 // GetOpenFileName will always change the current working directory on
1088 // (according to MSDN) "Windows NT 4.0/2000/XP" because the flag
1089 // OFN_NOCHANGEDIR has no effect. If the user did not specify
1090 // wxFD_CHANGE_DIR let's restore the current working directory to what it
1091 // was before the dialog was shown.
1092 if (msw_flags & OFN_NOCHANGEDIR)
1093 {
1094 wxSetWorkingDirectory(cwdOrig);
1095 }
1096
1097 m_fileNames.Empty();
1098
1099 if ((HasFdFlag(wxFD_MULTIPLE)) &&
1100#if defined(OFN_EXPLORER)
1101 ( fileNameBuffer[of.nFileOffset-1] == wxT('\0') )
1102#else
1103 ( fileNameBuffer[of.nFileOffset-1] == wxT(' ') )
1104#endif // OFN_EXPLORER
1105 )
1106 {
1107#if defined(OFN_EXPLORER)
1108 m_dir = fileNameBuffer;
1109 i = of.nFileOffset;
1110 m_fileName = &fileNameBuffer[i];
1111 m_fileNames.Add(m_fileName);
1112 i += m_fileName.Len() + 1;
1113
1114 while (fileNameBuffer[i] != wxT('\0'))
1115 {
1116 m_fileNames.Add(&fileNameBuffer[i]);
1117 i += wxStrlen(&fileNameBuffer[i]) + 1;
1118 }
1119#else
1120 wxStringTokenizer toke(fileNameBuffer, wxT(" \t\r\n"));
1121 m_dir = toke.GetNextToken();
1122 m_fileName = toke.GetNextToken();
1123 m_fileNames.Add(m_fileName);
1124
1125 while (toke.HasMoreTokens())
1126 m_fileNames.Add(toke.GetNextToken());
1127#endif // OFN_EXPLORER
1128
1129 wxString dir(m_dir);
1130 if ( m_dir.Last() != wxT('\\') )
1131 dir += wxT('\\');
1132
1133 m_path = dir + m_fileName;
1134 m_filterIndex = (int)of.nFilterIndex - 1;
1135 }
1136 else
1137 {
1138 //=== Adding the correct extension >>=================================
1139 m_filterIndex = (int)of.nFilterIndex - 1;
1140
1141 if ( !of.nFileExtension || fileNameBuffer[of.nFileExtension] == wxT('\0') )
1142 {
1143 // User has typed a filename without an extension:
1144 const wxChar* extension = filterBuffer;
1145 int maxFilter = (int)(of.nFilterIndex*2L) - 1;
1146
1147 for( int i = 0; i < maxFilter; i++ ) // get extension
1148 extension = extension + wxStrlen( extension ) + 1;
1149
1150 m_fileName = AppendExtension(fileNameBuffer, extension);
1151 wxStrlcpy(fileNameBuffer, m_fileName.c_str(), WXSIZEOF(fileNameBuffer));
1152 }
1153
1154 m_path = fileNameBuffer;
1155 m_fileName = wxFileNameFromPath(fileNameBuffer);
1156 m_fileNames.Add(m_fileName);
1157 m_dir = wxPathOnly(fileNameBuffer);
1158 }
1159
1160 return wxID_OK;
1161}
1162
1164{
1165 mRoot = NULL;
1166 mHwnd = (HWND) INVALID_HANDLE_VALUE;
1167 mModalCount = 0;
1168
1169 Register();
1170}
1171
1172void FileDialog::Disabler::Init(wxWindow *root, HWND hwnd)
1173{
1174 mRoot = root;
1175 mHwnd = hwnd;
1176}
1177
1178int FileDialog::Disabler::Enter(wxDialog *dialog)
1179{
1180 if (mHwnd != (HWND) INVALID_HANDLE_VALUE)
1181 {
1182 if (IsChild(dialog)) {
1183 ::EnableWindow(mHwnd, FALSE);
1184 mModalCount++;
1185 }
1186 }
1187
1188 return wxID_NONE;
1189}
1190
1191void FileDialog::Disabler::Exit(wxDialog *dialog)
1192{
1193 if (mHwnd != (HWND) INVALID_HANDLE_VALUE)
1194 {
1195 if (IsChild(dialog))
1196 {
1197 mModalCount--;
1198 if (mModalCount == 0)
1199 {
1200 ::EnableWindow(mHwnd, TRUE);
1201 ::SetWindowPos(mHwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
1202 }
1203 }
1204 }
1205}
1206
1207bool FileDialog::Disabler::IsChild(const wxDialog *dialog) const
1208{
1209 if (!dialog)
1210 {
1211 return false;
1212 }
1213
1214 for (const wxWindow *w = dialog->GetParent(); w != NULL; w = w->GetParent())
1215 {
1216 if (w == mRoot)
1217 {
1218 return true;
1219 }
1220 }
1221
1222 return false;
1223}
wxT("CloseDown"))
IMPLEMENT_CLASS(AudioSetupToolBar, ToolBar)
#define str(a)
wxString name
Definition: TagsEditor.cpp:166
static const auto fn
int Enter(wxDialog *dialog)
bool IsChild(const wxDialog *dialog) const
void Init(wxWindow *root, HWND hwnd)
void Exit(wxDialog *dialog)
void CreateUserPane(wxWindow *parent)
Definition: FileDialog.cpp:36
virtual bool HasUserPaneCreator() const
Definition: FileDialog.cpp:25
Dialog used to present platform specific "Save As" dialog with custom controls.
virtual void MSWOnDestroy(HWND hwnd, LPOPENFILENAME pOfn)
virtual void DoCentre(int dir)
virtual void DoGetPosition(int *x, int *y) const
FileDialog::Disabler mDisabler
static UINT_PTR APIENTRY ParentHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
virtual void SetFileExtension(const wxString &extension)
virtual void MSWOnFolderChange(HWND hwnd, LPOPENFILENAME pOfn)
wxArrayString m_FilterGroups
virtual void DoGetSize(int *width, int *height) const
virtual void MSWOnSelChange(HWND hwnd, LPOPENFILENAME pOfn)
virtual void MSWOnSize(HWND hwnd, LPOPENFILENAME pOfn)
virtual UINT_PTR MSWDialogHook(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam, OPENFILENAME *pOfn)
virtual void GetFilenames(wxArrayString &files) const
virtual void GetPaths(wxArrayString &paths) const
wxArrayString m_Filters
virtual void MSWOnGetMinMaxInfo(HWND hwnd, LPOPENFILENAME pOfn, LPMINMAXINFO pMmi)
wxArrayString m_fileNames
void FilterFiles(HWND hwnd, bool refresh)
virtual void MSWOnTypeChange(HWND hwnd, LPOPENFILENAME pOfn)
virtual void MSWOnInitDone(HWND hwnd, LPOPENFILENAME pOfn)
virtual void MSWOnInitDialog(HWND hwnd, LPOPENFILENAME pOfn)
virtual UINT_PTR MSWParentHook(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam, OPENFILENAME *pOfn)
static UINT_PTR APIENTRY DialogHook(HWND hDlg, UINT iMsg, WPARAM wParam, LPARAM lParam)
void ParseFilter(int index)
virtual int ShowModal()
virtual void DoMoveWindow(int x, int y, int width, int height)
static wxRect gs_rectDialog(0, 0, 428, 266)
static bool DoShowCommFileDialog(OPENFILENAME *of, long style, DWORD *err)
#define wxMAXFILE
#define wxMAXPATH
#define WM_GETISHELLBROWSER
static const DWORD wxOPENFILENAME_V4_SIZE
#define wxMAXEXT
static bool ShowCommFileDialog(OPENFILENAME *of, long style)
static DWORD gs_ofStructSize
static const DWORD wxOPENFILENAME_V5_SIZE