https://bugs.winehq.org/show_bug.cgi?id=24624
--- Comment #7 from Damjan Jovanovic damjan.jov@gmail.com --- In Audacity's git, tag Audacity-2.0.0, file lib-src/FileDialog/win/FileDialogPrivate.cpp:
---snip--- // We do not use the custom filter code on Windows 7 or higher since it now // handles filters larger than 260 characters. gs_customFilter = (major < 6 || minor < 1);
wxString filterBuffer;
for (i = 0; i < items ; i++) { filterBuffer += wildDescriptions[i]; filterBuffer += wxT("|"); filterBuffer += (gs_customFilter ? wxT("*.*") : m_FilterGroups[i]); filterBuffer += wxT("|"); } ---snip---
(Each '|' eventually becomes '\0').
So the file type mask is always "*.*" on Windows < 6.1, presumably to save space and allow many file type masks within the 260 character limit. That "gs_customFilter" global variable is also checked when notifications are delivered to its file dialog hook function:
---snip--- else if (CDN_TYPECHANGE == (pNotifyCode->hdr).code && gs_customFilter) { OPENFILENAME *ofn = (OPENFILENAME *) GetWindowLongPtr(hDlg, GWLP_USERDATA); FileDialog *me = (FileDialog *) ofn->lCustData; me->ParseFilter(ofn->nFilterIndex); me->FilterFiles(hDlg, true); } ---snip---
So on Windows < 6.1, we run all that. The "me->ParseFilter()" will populate "m_Filters" with the list of masks for the currently selected file type, while "me->FilterFiles()" does the filtering work based on those masks. How?
The lengthy FileDialog::FilterFiles() method looks up the "lst2" listview on the dialog box, iterates over its contents, then uses many shell32 APIs. In brief, it allows folders, otherwise gets the filename and tries to match it against each filter from the earlier "m_Filters", using wxMatchWild(). If no filters matched, ListView_DeleteItem() deletes that filename from the listview.
Finally, where I believe things go wrong:
---snip--- // On Vista and maybe XP, we seem to need to refresh the view after // changing the filters. But, only refresh for type changes and not // selection changes since it causes additional selection change // events to occur. if (ishellview && refresh) { ishellview->Refresh(); } ---snip---
To summarize, Audacity 2.0: 1. Expects the file save dialog listview to be fully populated when it is called, with *.* as the filter. 2. Wants to decide which files do not match, and expects itself to be able to delete them from the listview. 3. Expects IShellview::Refresh() not to change which files are displayed, after it does the deletion.
(2) works as long as (1) is satisfied, but Wine fails (1) as it populates the listview with IShellview::Refresh() AFTER sending CDN_TYPECHANGE, and fails (3) as its IShellview::Refresh() does re-add deleted items.
To hack this into working, it is enough to comment the "ishellview->Refresh()" from earlier, and patch Wine to call IShellView::Refresh() prior to sending CDN_TYPECHANGE:
---snip--- diff --git a/dlls/comdlg32/filedlg.c b/dlls/comdlg32/filedlg.c index 0ada09aad96..0ec1e2f3fe9 100644 --- a/dlls/comdlg32/filedlg.c +++ b/dlls/comdlg32/filedlg.c @@ -3194,13 +3194,18 @@ static BOOL FILEDLG95_FILETYPE_OnCommand(HWND hwnd, WORD wNotifyCode) len = lstrlenW(lpstrFilter)+1; fodInfos->ShellInfos.lpstrCurrentFilter = heap_alloc( len * sizeof(WCHAR) ); lstrcpyW(fodInfos->ShellInfos.lpstrCurrentFilter,lpstrFilter); + /* Refresh the actual view to display the included items*/ + if (fodInfos->Shell.FOIShellView) + IShellView_Refresh(fodInfos->Shell.FOIShellView); if(fodInfos->ofnInfos->Flags & OFN_EXPLORER) SendCustomDlgNotificationMessage(hwnd,CDN_TYPECHANGE); } - - /* Refresh the actual view to display the included items*/ - if (fodInfos->Shell.FOIShellView) - IShellView_Refresh(fodInfos->Shell.FOIShellView); + else + { + /* Refresh the actual view to display the included items*/ + if (fodInfos->Shell.FOIShellView) + IShellView_Refresh(fodInfos->Shell.FOIShellView); + } } } return FALSE; ---snip---
A real fix would involve changes to IShellView::Refresh() itself, whose real function is currently a mystery.