comdlg32: Correctly handle filters with multiple file extensions in Save As dialogs (try 3)
Alex Henrie
alexhenrie24 at gmail.com
Fri Jan 20 15:01:04 CST 2012
Fixes bug 29362.
The MSDN documentation of this behavior can be found at
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646960%28v=vs.85%29.aspx#_win32_Filters
---
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 9b725ba..76b8e45 100644
--- a/dlls/comdlg32/filedlg.c
+++ b/dlls/comdlg32/filedlg.c
@@ -2534,12 +2534,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);
@@ -2549,15 +2549,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);
@@ -2565,20 +2589,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 8539d3d..0d1112f 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");
}
}
--
1.7.4.1
More information about the wine-patches
mailing list