From 7a34094e6e3e8a321148c64b0c9e2b986e63a7d9 Mon Sep 17 00:00:00 2001 From: Alex Henrie Date: Fri, 20 Jan 2012 14:52:18 -0700 Subject: [PATCH] comdlg32: Correctly handle filters with multiple file extensions in Save As dialogs. --- dlls/comdlg32/filedlg.c | 61 +++++++++++++++++++++---------- dlls/comdlg32/tests/filedlg.c | 85 +++++++++++++++++++++++++++++++------------ 2 files changed, 104 insertions(+), 42 deletions(-) diff --git a/dlls/comdlg32/filedlg.c b/dlls/comdlg32/filedlg.c index 43df312fe79..c31641b9072 100644 --- a/dlls/comdlg32/filedlg.c +++ b/dlls/comdlg32/filedlg.c @@ -2533,12 +2533,12 @@ BOOL FILEDLG95_OnOpen(HWND hwnd) /* Attach the file extension with file name*/ ext = PathFindExtensionW(lpstrPathAndFile); - if (! *ext) + if (! *ext && fodInfos->defext) { /* if no extension is specified with file name, then */ /* attach the extension from file filter or default one */ - const WCHAR *filterExt = NULL; + WCHAR *filterExt = NULL; LPWSTR lpstrFilter = NULL; static const WCHAR szwDot[] = {'.',0}; int PathLength = lstrlenW(lpstrPathAndFile); @@ -2548,15 +2548,39 @@ BOOL FILEDLG95_OnOpen(HWND hwnd) fodInfos->ofnInfos->nFilterIndex-1); if (lpstrFilter != (LPWSTR)CB_ERR) /* control is not empty */ - filterExt = PathFindExtensionW(lpstrFilter); + { + WCHAR* filterAtSemicolon; + filterExt = HeapAlloc(GetProcessHeap(), 0, lstrlenW(lpstrFilter) * sizeof(WCHAR) + sizeof(WCHAR)); + strcpyW(filterExt, lpstrFilter); + + /* if a semicolon-separated list of file extensions was given, do not include the + semicolon or anything after it in the extension. + example: if filterExt was "*.abc;*.def", it will become "*.abc" */ + filterAtSemicolon = strchrW(filterExt, ';'); + if (filterAtSemicolon) + { + filterAtSemicolon[0] = '\0'; + } + + /* strip the * or anything else from the extension, "*.abc" becomes "abc" */ + strcpyW(filterExt, PathFindExtensionW(filterExt) + 1); + + /* if the extension contains a glob, ignore it */ + if (strchrW(filterExt, '*') || strchrW(filterExt, '?')) + { + HeapFree(GetProcessHeap(), 0, filterExt); + filterExt = NULL; + } + } - if ( filterExt && *filterExt ) /* attach the file extension from file type filter*/ - filterExt = filterExt + 1; - else if ( fodInfos->defext ) /* attach the default file extension*/ - filterExt = fodInfos->defext; + if (!filterExt) + { + /* use the default file extension */ + filterExt = HeapAlloc(GetProcessHeap(), 0, lstrlenW(fodInfos->defext) * sizeof(WCHAR) + sizeof(WCHAR)); + strcpyW(filterExt, fodInfos->defext); + } - /* If extension contains a glob, ignore it */ - if ( filterExt && !strchrW(filterExt, '*') && !strchrW(filterExt, '?') ) + if (*filterExt) /* ignore filterExt="" */ { /* Attach the dot*/ lstrcatW(lpstrPathAndFile, szwDot); @@ -2564,20 +2588,19 @@ BOOL FILEDLG95_OnOpen(HWND hwnd) lstrcatW(lpstrPathAndFile, filterExt ); } + HeapFree(GetProcessHeap(), 0, filterExt); + /* In Open dialog: if file does not exist try without extension */ if (!(fodInfos->DlgInfos.dwDlgProp & FODPROP_SAVEDLG) && !PathFileExistsW(lpstrPathAndFile)) lpstrPathAndFile[PathLength] = '\0'; - } - if (fodInfos->defext) /* add default extension */ - { - /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */ - if (*ext) - ext++; - if (!lstrcmpiW(fodInfos->defext, ext)) - fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT; - else - fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT; + /* Set/clear the output OFN_EXTENSIONDIFFERENT flag */ + if (*ext) + ext++; + if (!lstrcmpiW(fodInfos->defext, ext)) + fodInfos->ofnInfos->Flags &= ~OFN_EXTENSIONDIFFERENT; + else + fodInfos->ofnInfos->Flags |= OFN_EXTENSIONDIFFERENT; } /* In Save dialog: check if the file already exists */ diff --git a/dlls/comdlg32/tests/filedlg.c b/dlls/comdlg32/tests/filedlg.c index 8539d3d60ba..0d1112f9052 100644 --- a/dlls/comdlg32/tests/filedlg.c +++ b/dlls/comdlg32/tests/filedlg.c @@ -1044,51 +1044,90 @@ static UINT_PTR WINAPI test_extension_wndproc(HWND dlg, UINT msg, WPARAM wParam, return FALSE; } -static const char *defext_filters[] = { - "TestFilter (*.pt*)\0*.pt*\0", - "TestFilter (*.ab?)\0*.ab?\0", - "TestFilter (*.*)\0*.*\0", - NULL /* is a test, not an endmark! */ -}; - #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0])) +static void test_extension_helper(OPENFILENAME* ofn, const char *filter, + const char *expected_filename) +{ + char *filename_ptr; + DWORD ret; + BOOL boolret; + + strcpy(ofn->lpstrFile, "deadbeef"); + ofn->lpstrFilter = filter; + + boolret = GetSaveFileNameA(ofn); + ok(boolret, "%s: expected TRUE\n", filter); + + ret = CommDlgExtendedError(); + ok(!ret, "%s: CommDlgExtendedError returned %#x\n", filter, ret); + + filename_ptr = ofn->lpstrFile + ofn->nFileOffset; + ok(strcmp(filename_ptr, expected_filename) == 0, + "%s: Filename is %s, expected %s\n", filter, filename_ptr, expected_filename); +} + static void test_extension(void) { OPENFILENAME ofn = { sizeof(OPENFILENAME)}; char filename[1024] = {0}; char curdir[MAX_PATH]; - char *filename_ptr; - const char *test_file_name = "deadbeef"; unsigned int i; - DWORD ret; BOOL boolret; + const char *defext_concrete_filters[] = { + "TestFilter (*.abc)\0*.abc\0", + "TestFilter (*.abc;)\0*.abc;\0", + "TestFilter (*.abc;*.def)\0*.abc;*.def\0", + }; + + const char *defext_wildcard_filters[] = { + "TestFilter (*.pt*)\0*.pt*\0", + "TestFilter (*.pt*;*.abc)\0*.pt*;*.abc\0", + "TestFilter (*.ab?)\0*.ab?\0", + "TestFilter (*.*)\0*.*\0", + NULL /* is a test, not an endmark! */ + }; + boolret = GetCurrentDirectoryA(sizeof(curdir), curdir); ok(boolret, "Failed to get current dir err %d\n", GetLastError()); - /* Ignore .* extension */ ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = NULL; ofn.lpstrFile = filename; ofn.nMaxFile = MAX_PATH; ofn.Flags = OFN_EXPLORER | OFN_ENABLEHOOK; - ofn.lpstrDefExt = NULL; ofn.lpstrInitialDir = curdir; ofn.lpfnHook = test_extension_wndproc; ofn.nFileExtension = 0; - for (i = 0; i < ARRAY_SIZE(defext_filters); i++) { - ofn.lpstrFilter = defext_filters[i]; - strcpy(filename, test_file_name); - boolret = GetSaveFileNameA(&ofn); - ok(boolret, "%u: expected true\n", i); - ret = CommDlgExtendedError(); - ok(!ret, "%u: CommDlgExtendedError returned %#x\n", i, ret); - filename_ptr = ofn.lpstrFile + strlen( ofn.lpstrFile ) - strlen( test_file_name ); - ok( strlen(ofn.lpstrFile) >= strlen(test_file_name), "Filename %s is too short\n", ofn.lpstrFile ); - ok( strcmp(filename_ptr, test_file_name) == 0, - "Filename is %s, expected %s\n", filename_ptr, test_file_name ); + ofn.lpstrDefExt = NULL; + + /* Without lpstrDefExt, append no extension */ + test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=NULL\0*.abc\0", "deadbeef"); + test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=NULL\0*.ab?\0", "deadbeef"); + + ofn.lpstrDefExt = ""; + + /* If lpstrDefExt="" and the filter has a concrete extension, append it */ + test_extension_helper(&ofn, "TestFilter (*.abc) lpstrDefExt=\"\"\0*.abc\0", "deadbeef.abc"); + + /* If lpstrDefExt="" and the filter has a wildcard extension, do nothing */ + test_extension_helper(&ofn, "TestFilter (*.ab?) lpstrDefExt=\"\"\0*.ab?\0", "deadbeef"); + + ofn.lpstrDefExt = "xyz"; + + /* Append concrete extensions from filters */ + for (i = 0; i < ARRAY_SIZE(defext_concrete_filters); i++) { + test_extension_helper(&ofn, defext_concrete_filters[i], "deadbeef.abc"); + } + + /* Append nothing from this filter */ + test_extension_helper(&ofn, "TestFilter (*.)\0*.\0", "deadbeef"); + + /* Ignore wildcard extensions in filters */ + for (i = 0; i < ARRAY_SIZE(defext_wildcard_filters); i++) { + test_extension_helper(&ofn, defext_wildcard_filters[i], "deadbeef.xyz"); } } -- 2.11.4.GIT