diff --git a/dlls/comdlg32/cdlg.h b/dlls/comdlg32/cdlg.h index e85ba9ff37c..91491d32290 100644 --- a/dlls/comdlg32/cdlg.h +++ b/dlls/comdlg32/cdlg.h @@ -178,6 +178,7 @@ void COMDLG32_GetCanonicalPath(PCIDLIST_ABSOLUTE pidlAbsCurrent, LPWSTR lpstrFil int FILEDLG95_ValidatePathAction(LPWSTR lpstrPathAndFile, IShellFolder **ppsf, HWND hwnd, DWORD flags, BOOL isSaveDlg, int defAction) DECLSPEC_HIDDEN; int COMDLG32_SplitFileNames(LPWSTR lpstrEdit, UINT nStrLen, LPWSTR *lpstrFileList, UINT *sizeUsed) DECLSPEC_HIDDEN; +void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText) DECLSPEC_HIDDEN; /* ITEMIDLIST */ diff --git a/dlls/comdlg32/filedlg.c b/dlls/comdlg32/filedlg.c index a36cd35bb90..28d7c777047 100644 --- a/dlls/comdlg32/filedlg.c +++ b/dlls/comdlg32/filedlg.c @@ -2259,7 +2259,7 @@ static void FILEDLG95_MRU_load_filename(LPWSTR stored_path) TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path)); } -static void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText) +void FILEDLG95_OnOpenMessage(HWND hwnd, int idCaption, int idText) { WCHAR strMsgTitle[MAX_PATH]; WCHAR strMsgText [MAX_PATH]; diff --git a/dlls/comdlg32/itemdlg.c b/dlls/comdlg32/itemdlg.c index a04b86adf79..80b0d86dede 100644 --- a/dlls/comdlg32/itemdlg.c +++ b/dlls/comdlg32/itemdlg.c @@ -87,6 +87,7 @@ typedef struct FileDialogImpl { DWORD ebevents_cookie; LPWSTR set_filename; + LPWSTR default_ext; LPWSTR custom_title; LPWSTR custom_okbutton; LPWSTR custom_cancelbutton; @@ -271,6 +272,21 @@ static void fill_filename_from_selection(FileDialogImpl *This) return; } +static LPWSTR get_first_ext_from_spec(LPWSTR buf, LPCWSTR spec) +{ + WCHAR *endpos, *ext; + + lstrcpyW(buf, spec); + if( (endpos = StrChrW(buf, ';')) ) + *endpos = '\0'; + + ext = PathFindExtensionW(buf); + if(StrChrW(ext, '*')) + return NULL; + + return ext; +} + static HRESULT on_default_action(FileDialogImpl *This) { IShellFolder *psf_parent, *psf_desktop; @@ -316,9 +332,62 @@ static HRESULT on_default_action(FileDialogImpl *This) open_action = ONOPEN_BROWSE; open_action = FILEDLG95_ValidatePathAction(canon_filename, &psf_parent, This->dlg_hwnd, - This->options, (This->dlg_type == ITEMDLG_TYPE_SAVE), + This->options & ~FOS_FILEMUSTEXIST, + (This->dlg_type == ITEMDLG_TYPE_SAVE), open_action); + /* Add the proper extension */ + if(open_action == ONOPEN_OPEN) + { + static const WCHAR dotW[] = {'.',0}; + + if(This->dlg_type == ITEMDLG_TYPE_SAVE) + { + WCHAR extbuf[MAX_PATH], *newext = NULL; + + if(This->filterspec_count) + { + newext = get_first_ext_from_spec(extbuf, This->filterspecs[This->filetypeindex].pszSpec); + } + else if(This->default_ext) + { + lstrcpyW(extbuf, dotW); + lstrcatW(extbuf, This->default_ext); + newext = extbuf; + } + + if(newext) + { + WCHAR *ext = PathFindExtensionW(canon_filename); + if(lstrcmpW(ext, newext)) + lstrcatW(canon_filename, newext); + } + } + else + { + if( !(This->options & FOS_NOVALIDATE) && (This->options & FOS_FILEMUSTEXIST) && + !PathFileExistsW(canon_filename)) + { + if(This->default_ext) + { + lstrcatW(canon_filename, dotW); + lstrcatW(canon_filename, This->default_ext); + + if(!PathFileExistsW(canon_filename)) + { + FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING); + open_action = ONOPEN_BROWSE; + } + } + else + { + FILEDLG95_OnOpenMessage(This->dlg_hwnd, 0, IDS_FILENOTEXISTING); + open_action = ONOPEN_BROWSE; + } + } + } + } + pidla[i] = COMDLG32_SHSimpleIDListFromPathAW(canon_filename); if(psf_parent && !(open_action == ONOPEN_BROWSE)) @@ -959,6 +1028,7 @@ static ULONG WINAPI IFileDialog2_fnRelease(IFileDialog2 *iface) if(This->psia_results) IShellItemArray_Release(This->psia_results); LocalFree(This->set_filename); + LocalFree(This->default_ext); LocalFree(This->custom_title); LocalFree(This->custom_okbutton); LocalFree(This->custom_cancelbutton); @@ -1281,8 +1351,12 @@ static HRESULT WINAPI IFileDialog2_fnAddPlace(IFileDialog2 *iface, IShellItem *p static HRESULT WINAPI IFileDialog2_fnSetDefaultExtension(IFileDialog2 *iface, LPCWSTR pszDefaultExtension) { FileDialogImpl *This = impl_from_IFileDialog2(iface); - FIXME("stub - %p (%s)\n", This, debugstr_w(pszDefaultExtension)); - return E_NOTIMPL; + TRACE("%p (%s)\n", This, debugstr_w(pszDefaultExtension)); + + LocalFree(This->default_ext); + This->default_ext = StrDupW(pszDefaultExtension); + + return S_OK; } static HRESULT WINAPI IFileDialog2_fnClose(IFileDialog2 *iface, HRESULT hr) @@ -2316,6 +2390,7 @@ static HRESULT FileDialog_constructor(IUnknown *pUnkOuter, REFIID riid, void **p fdimpl->peb = NULL; fdimpl->set_filename = NULL; + fdimpl->default_ext = NULL; fdimpl->custom_cancelbutton = fdimpl->custom_filenamelabel = NULL; /* FIXME: The default folder setting should be restored for the diff --git a/dlls/comdlg32/tests/itemdlg.c b/dlls/comdlg32/tests/itemdlg.c index 7c528680597..5d438aea3ee 100644 --- a/dlls/comdlg32/tests/itemdlg.c +++ b/dlls/comdlg32/tests/itemdlg.c @@ -27,6 +27,7 @@ static HRESULT (WINAPI *pSHCreateShellItem)(LPCITEMIDLIST,IShellFolder*,LPCITEMIDLIST,IShellItem**); static HRESULT (WINAPI *pSHGetIDListFromObject)(IUnknown*, PIDLIST_ABSOLUTE*); +static HRESULT (WINAPI *pSHCreateItemFromParsingName)(PCWSTR,IBindCtx*,REFIID,void**); static void init_function_pointers(void) { @@ -35,6 +36,7 @@ static void init_function_pointers(void) #define MAKEFUNC(f) (p##f = (void*)GetProcAddress(hmod, #f)) MAKEFUNC(SHCreateShellItem); MAKEFUNC(SHGetIDListFromObject); + MAKEFUNC(SHCreateItemFromParsingName); #undef MAKEFUNC } @@ -48,6 +50,8 @@ typedef struct { LONG OnFileOk, OnFolderChanging, OnFolderChange; LONG OnSelectionChange, OnShareViolation, OnTypeChange; LONG OnOverwrite; + LPCWSTR set_filename; + BOOL set_filename_tried; } IFileDialogEventsImpl; static inline IFileDialogEventsImpl *impl_from_IFileDialogEvents(IFileDialogEvents *iface) @@ -99,6 +103,34 @@ static HRESULT WINAPI IFileDialogEvents_fnOnFolderChange(IFileDialogEvents *ifac { IFileDialogEventsImpl *This = impl_from_IFileDialogEvents(iface); This->OnFolderChange++; + + if(This->set_filename) + { + IOleWindow *pow; + HWND dlg_hwnd; + HRESULT hr; + BOOL br; + + hr = IFileDialog_QueryInterface(pfd, &IID_IOleWindow, (void**)&pow); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + hr = IOleWindow_GetWindow(pow, &dlg_hwnd); + ok(hr == S_OK, "Got 0x%08x\n", hr); + ok(dlg_hwnd != NULL, "Got NULL.\n"); + + IOleWindow_Release(pow); + + hr = IFileDialog_SetFileName(pfd, This->set_filename); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + if(!This->set_filename_tried) + { + br = PostMessageW(dlg_hwnd, WM_COMMAND, IDOK, 0); + ok(br, "Failed\n"); + This->set_filename_tried = TRUE; + } + } + return S_OK; } @@ -523,8 +555,6 @@ static void test_basics(void) ok(hr == S_OK, "got 0x%08x.\n", hr); /* SetDefaultExtension */ - todo_wine - { hr = IFileOpenDialog_SetDefaultExtension(pfod, NULL); ok(hr == S_OK, "got 0x%08x.\n", hr); hr = IFileOpenDialog_SetDefaultExtension(pfod, txt); @@ -538,7 +568,6 @@ static void test_basics(void) ok(hr == S_OK, "got 0x%08x.\n", hr); hr = IFileSaveDialog_SetDefaultExtension(pfsd, null); ok(hr == S_OK, "got 0x%08x.\n", hr); - } /* SetDefaultFolder */ hr = IFileOpenDialog_SetDefaultFolder(pfod, NULL); @@ -921,6 +950,245 @@ static void test_advise(void) ok(!ref, "Got refcount %d, should have been released.\n", ref); } +static void touch_file(LPCWSTR filename) +{ + HANDLE file; + file = CreateFileW(filename, GENERIC_WRITE, 0, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL); + ok(file != INVALID_HANDLE_VALUE, "Failed to create file.\n"); + CloseHandle(file); +} + +static void test_filename_savedlg_(LPCWSTR set_filename, LPCWSTR defext, + const COMDLG_FILTERSPEC *filterspec, UINT fs_count, + LPCWSTR exp_filename, const char *file, int line) +{ + IFileSaveDialog *pfsd; + IFileDialogEventsImpl *pfdeimpl; + IFileDialogEvents *pfde; + DWORD cookie; + LPWSTR filename; + IShellItem *psi; + LONG ref; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, + &IID_IFileSaveDialog, (void**)&pfsd); + ok_(file,line)(hr == S_OK, "Got 0x%08x\n", hr); + + if(fs_count) + { + hr = IFileSaveDialog_SetFileTypes(pfsd, fs_count, filterspec); + ok_(file,line)(hr == S_OK, "SetFileTypes failed: Got 0x%08x\n", hr); + } + + if(defext) + { + hr = IFileSaveDialog_SetDefaultExtension(pfsd, defext); + ok_(file,line)(hr == S_OK, "SetDefaultExtensions failed: Got 0x%08x\n", hr); + } + + pfde = IFileDialogEvents_Constructor(); + pfdeimpl = impl_from_IFileDialogEvents(pfde); + pfdeimpl->set_filename = set_filename; + hr = IFileSaveDialog_Advise(pfsd, pfde, &cookie); + ok_(file,line)(hr == S_OK, "Advise failed: Got 0x%08x\n", hr); + + hr = IFileSaveDialog_Show(pfsd, NULL); + ok_(file,line)(hr == S_OK, "Show failed: Got 0x%08x\n", hr); + + hr = IFileSaveDialog_GetFileName(pfsd, &filename); + ok_(file,line)(hr == S_OK, "GetFileName failed: Got 0x%08x\n", hr); + ok_(file,line)(!lstrcmpW(filename, set_filename), "Got %s\n", wine_dbgstr_w(filename)); + CoTaskMemFree(filename); + + hr = IFileSaveDialog_GetResult(pfsd, &psi); + ok_(file,line)(hr == S_OK, "GetResult failed: Got 0x%08x\n", hr); + + hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename); + ok_(file,line)(hr == S_OK, "GetDisplayName failed: Got 0x%08x\n", hr); + ok_(file,line)(!lstrcmpW(filename, exp_filename), "(GetDisplayName) Got %s\n", wine_dbgstr_w(filename)); + CoTaskMemFree(filename); + IShellItem_Release(psi); + + hr = IFileSaveDialog_Unadvise(pfsd, cookie); + ok_(file,line)(hr == S_OK, "Unadvise failed: Got 0x%08x\n", hr); + + ref = IFileSaveDialog_Release(pfsd); + ok_(file,line)(!ref, "Got refcount %d, should have been released.\n", ref); + + IFileDialogEvents_Release(pfde); +} +#define test_filename_savedlg(set_filename, defext, filterspec, fs_count, exp_filename) \ + test_filename_savedlg_(set_filename, defext, filterspec, fs_count, exp_filename, __FILE__, __LINE__) + +static void test_filename_opendlg_(LPCWSTR set_filename, IShellItem *psi_current, LPCWSTR defext, + const COMDLG_FILTERSPEC *filterspec, UINT fs_count, + LPCWSTR exp_filename, const char *file, int line) +{ + IFileOpenDialog *pfod; + IFileDialogEventsImpl *pfdeimpl; + IFileDialogEvents *pfde; + DWORD cookie; + LPWSTR filename; + IShellItemArray *psia; + IShellItem *psi; + LONG ref; + HRESULT hr; + + hr = CoCreateInstance(&CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, + &IID_IFileOpenDialog, (void**)&pfod); + ok_(file,line)(hr == S_OK, "CoCreateInstance failed: Got 0x%08x\n", hr); + + if(defext) + { + hr = IFileOpenDialog_SetDefaultExtension(pfod, defext); + ok_(file,line)(hr == S_OK, "SetDefaultExtensions failed: Got 0x%08x\n", hr); + } + + if(fs_count) + { + hr = IFileOpenDialog_SetFileTypes(pfod, 2, filterspec); + ok_(file,line)(hr == S_OK, "SetFileTypes failed: Got 0x%08x\n", hr); + } + + hr = IFileOpenDialog_SetFolder(pfod, psi_current); + ok_(file,line)(hr == S_OK, "SetFolder failed: Got 0x%08x\n", hr); + + pfde = IFileDialogEvents_Constructor(); + pfdeimpl = impl_from_IFileDialogEvents(pfde); + pfdeimpl->set_filename = set_filename; + pfdeimpl->set_filename_tried = FALSE; + hr = IFileOpenDialog_Advise(pfod, pfde, &cookie); + ok_(file,line)(hr == S_OK, "Advise failed: Got 0x%08x\n", hr); + + hr = IFileOpenDialog_Show(pfod, NULL); + ok_(file,line)(hr == S_OK || (!exp_filename && hr == HRESULT_FROM_WIN32(ERROR_CANCELLED)), + "Show failed: Got 0x%08x\n", hr); + if(hr == S_OK) + { + hr = IFileOpenDialog_GetResult(pfod, &psi); + ok_(file,line)(hr == S_OK, "GetResult failed: Got 0x%08x\n", hr); + + hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename); + ok_(file,line)(hr == S_OK, "GetDisplayName(Result) failed: Got 0x%08x\n", hr); + ok_(file,line)(!lstrcmpW(filename, exp_filename), "(GetResult) Got %s\n", wine_dbgstr_w(filename)); + CoTaskMemFree(filename); + IShellItem_Release(psi); + + hr = IFileOpenDialog_GetResults(pfod, &psia); + ok_(file,line)(hr == S_OK, "GetResults failed: Got 0x%08x\n", hr); + hr = IShellItemArray_GetItemAt(psia, 0, &psi); + ok_(file,line)(hr == S_OK, "GetItemAt failed: Got 0x%08x\n", hr); + + hr = IShellItem_GetDisplayName(psi, SIGDN_PARENTRELATIVEPARSING, &filename); + ok_(file,line)(hr == S_OK, "GetDisplayName(Results) failed: Got 0x%08x\n", hr); + ok_(file,line)(!lstrcmpW(filename, exp_filename), "(GetResults) Got %s\n", wine_dbgstr_w(filename)); + CoTaskMemFree(filename); + + IShellItem_Release(psi); + IShellItemArray_Release(psia); + } + else + { + hr = IFileOpenDialog_GetResult(pfod, &psi); + ok_(file,line)(hr == E_UNEXPECTED, "GetResult: Got 0x%08x\n", hr); + + hr = IFileOpenDialog_GetResults(pfod, &psia); + ok_(file,line)(hr == E_FAIL, "GetResults: Got 0x%08x\n", hr); + } + + hr = IFileOpenDialog_GetFileName(pfod, &filename); + ok_(file,line)(hr == S_OK, "GetFileName failed: Got 0x%08x\n", hr); + ok_(file,line)(!lstrcmpW(filename, set_filename), "(GetFileName) Got %s\n", wine_dbgstr_w(filename)); + CoTaskMemFree(filename); + + + hr = IFileOpenDialog_Unadvise(pfod, cookie); + ok_(file,line)(hr == S_OK, "Unadvise failed: Got 0x%08x\n", hr); + + ref = IFileOpenDialog_Release(pfod); + ok_(file,line)(!ref, "Got refcount %d, should have been released.\n", ref); + + IFileDialogEvents_Release(pfde); +} +#define test_filename_opendlg(set_filename, psi, defext, filterspec, fs_count, exp_filename) \ + test_filename_opendlg_(set_filename, psi, defext, filterspec, fs_count, exp_filename, __FILE__, __LINE__) + +static void test_filename(void) +{ + IShellItem *psi_current; + HRESULT hr; + WCHAR buf[MAX_PATH]; + + static const WCHAR filename_noextW[] = {'w','i','n','e','t','e','s','t',0}; + static const WCHAR filename_dotextW[] = {'w','i','n','e','t','e','s','t','.',0}; + static const WCHAR filename_dotanddefW[] = {'w','i','n','e','t','e','s','t','.','.','w','t','e',0}; + static const WCHAR filename_defextW[] = {'w','i','n','e','t','e','s','t','.','w','t','e',0}; + static const WCHAR filename_ext1W[] = {'w','i','n','e','t','e','s','t','.','w','t','1',0}; + static const WCHAR filename_ext2W[] = {'w','i','n','e','t','e','s','t','.','w','t','2',0}; + static const WCHAR filename_ext1anddefW[] = + {'w','i','n','e','t','e','s','t','.','w','t','1','.','w','t','e',0}; + static const WCHAR defextW[] = {'w','t','e',0}; + static const WCHAR desc1[] = {'d','e','s','c','r','i','p','t','i','o','n','1',0}; + static const WCHAR desc2[] = {'d','e','s','c','r','i','p','t','i','o','n','2',0}; + static const WCHAR descdef[] = {'d','e','f','a','u','l','t',' ','d','e','s','c',0}; + static const WCHAR ext1[] = {'*','.','w','t','1',0}; + static const WCHAR ext2[] = {'*','.','w','t','2',0}; + static const WCHAR extdef[] = {'*','.','w','t','e',0}; + static const WCHAR complexext[] = {'*','.','w','t','2',';','*','.','w','t','1',0}; + + static const COMDLG_FILTERSPEC filterspec[] = { + { desc1, ext1 }, { desc2, ext2 }, { descdef, extdef } + }; + static const COMDLG_FILTERSPEC filterspec2[] = { + { desc1, complexext } + }; + + /* No extension */ + test_filename_savedlg(filename_noextW, NULL, NULL, 0, filename_noextW); + /* Default extension */ + test_filename_savedlg(filename_noextW, defextW, NULL, 0, filename_defextW); + /* Default extension on filename ending with a . */ + test_filename_savedlg(filename_dotextW, defextW, NULL, 0, filename_dotanddefW); + /* Default extension on filename with default extension */ + test_filename_savedlg(filename_defextW, defextW, NULL, 0, filename_defextW); + /* Default extension on filename with another extension */ + test_filename_savedlg(filename_ext1W, defextW, NULL, 0, filename_ext1anddefW); + /* Default extension, filterspec without default extension */ + test_filename_savedlg(filename_noextW, defextW, filterspec, 2, filename_ext1W); + /* Default extension, filterspec with default extension */ + test_filename_savedlg(filename_noextW, defextW, filterspec, 3, filename_ext1W); + /* Default extension, filterspec with "complex" extension */ + test_filename_savedlg(filename_noextW, defextW, filterspec2, 1, filename_ext2W); + + GetCurrentDirectoryW(MAX_PATH, buf); + ok(!!pSHCreateItemFromParsingName, "SHCreateItemFromParsingName is missing.\n"); + hr = pSHCreateItemFromParsingName(buf, NULL, &IID_IShellItem, (void**)&psi_current); + ok(hr == S_OK, "Got 0x%08x\n", hr); + + touch_file(filename_noextW); + touch_file(filename_defextW); + touch_file(filename_ext2W); + + /* IFileOpenDialog, default extension */ + test_filename_opendlg(filename_noextW, psi_current, defextW, NULL, 0, filename_noextW); + /* IFileOpenDialog, default extension and filterspec */ + test_filename_opendlg(filename_noextW, psi_current, defextW, filterspec, 2, filename_noextW); + + DeleteFileW(filename_noextW); + /* IFileOpenDialog, default extension, noextW deleted */ + test_filename_opendlg(filename_noextW, psi_current, defextW, NULL, 0, filename_defextW); + if(0) /* Interactive */ + { + /* IFileOpenDialog, filterspec, no default extension, noextW deleted */ + test_filename_opendlg(filename_noextW, psi_current, NULL, filterspec, 2, NULL); + } + + IShellItem_Release(psi_current); + DeleteFileW(filename_defextW); + DeleteFileW(filename_ext2W); +} + START_TEST(itemdlg) { OleInitialize(NULL); @@ -930,6 +1198,7 @@ START_TEST(itemdlg) { test_basics(); test_advise(); + test_filename(); } else skip("Skipping all Item Dialog tests.\n");