Bug fix for win32 file chooser not supporting multiple file type filters

So I noticed in the native win32 FileChooser, the file pattern string is ignored and not parsed - so you essentially only get one file type entry for the native file dialog. After looking at the code I can see why, and I have a potential fix, although maybe it could use some help being cleaned up.

Anyways, the offending lines are here:

We can see here it is hardcoded to just have one file filter entry in the form of the
COMDLG_FILTERSPEC ‘spec’ array:

    const COMDLG_FILTERSPEC spec[] { { filtersString.toWideCharPointer(), filtersString.toWideCharPointer() } };


    if (! selectsDirectories && FAILED (dialog.SetFileTypes (numElementsInArray (spec), spec)))
        return false;


    return dialog.Show (static_cast<HWND> (async ? nullptr : owner->getWindowHandle())) == S_OK;

My fix below probably could use some help being cleaned up, but it works for me:

    auto extension = getDefaultFileExtension (filename);

    if (extension.isNotEmpty() && FAILED (dialog.SetDefaultExtension (extension.toWideCharPointer())))
        return false;

    // Parse filter string
    StringArray filterResults;
    filterResults.addTokens(filtersString.toLowerCase(), ";,", "\"'");
    filterResults.trim();
    filterResults.removeEmptyStrings();

    // special case for *.*, because people use it to mean "any file", but it
    // would actually ignore files with no extension.
    for (auto& r : filterResults)
        if (r == "*.*")
            r = "*";

    auto numFilters = filterResults.size();
    
    COMDLG_FILTERSPEC* spec = (COMDLG_FILTERSPEC*)malloc(sizeof(COMDLG_FILTERSPEC) * numFilters);

    for (int i = 0; i < numFilters; i++)
    {
        auto& fr = filterResults.getReference(i);
        spec[i] = { fr.toWideCharPointer(), fr.toWideCharPointer() };
    }

    if (!selectsDirectories && FAILED(dialog.SetFileTypes(filterResults.size(), spec)))
    {
        free(spec);
        return false;
    }

    free(spec);
    return dialog.Show (static_cast<HWND> (async ? nullptr : owner->getWindowHandle())) == S_OK;

The first WCHAR* in the spec array is the human readable lable/description, the second entry is the file pattern (*.png) - since I’m trying to keep it simple and backwards compatible, the same pattern value is used for both values.

So this fix allows native support for multiple file type filters in native win32 file choose dialog.

This also relates to my FR: Improve Native FileChooser File Filter Handling

Please vote!

Also, it should be said that this kind of filtering should be made cross-platform… It’s probably not the same across the board in terms of what can be done, or what JUCE could fill out.

2 Likes

I’ve played with this a bit, and I don’t think the pattern string is ignored. If I set a pattern like "*.jpg;*.jpeg;*.png;*.gif" and then create a FileChooser in ‘open’ mode, it only displays folders, and files with matching extensions.

For this reason, I think that the dialog is working as intended, given the current JUCE API - it’s only possible to supply a single filter string, and putting all filters into a single combo-box entry feels equally arbitrary to placing each filter into a separate item. I’d be reluctant to change the current behaviour (which hasn’t changed in years) without a strong argument that the current behaviour is broken.

If we address this issue to allow multiple file-extension entries, I’d prefer to do it by providing a new JUCE API which allows specifying the combo-box entries explicitly, with both readable strings and grouped file extensions.

I agree, this sounds like the way to go.

When I played with it, it explicitly did not work with a single line with multiple entries, which is why I posted this. However after playing with it again based on your response, it turns out that semicolon separated lists like in your example work, but comma separated lists do not. JUCE’s documentation around this gave me the impression that both comma and semicolon file type lists should work the same. Perhaps that’s the bug or at least an update to documentation is in order?

From JUCE’s current API documentation on FileChooser:

filePatternsAllowed: a set of file patterns to specify which files can be selected - each pattern should be separated by a comma or semi-colon, e.g. "*" or "*.jpg;*.gif". The native MacOS file browser only supports wildcard that specify extensions, so "*.jpg" is OK but "myfilename*" will not work. An empty string means that all files are allowed

I’m guessing this is a Windows issue and that the Windows file dialog allows a single filter string if it is semicolon separated, but does not respect commas. When I looked at the win32 code, IIRC there is no use of WildcardFileFilter or anything like that, the original unparsed filter string is sent in to the win32 native implementation, and presumably whatever semicolon filtering happens is internal to the win32 dialog.

1 Like

Good catch, I’ll fix the current version to allow comma separators too. Thanks!