comdlg32: Store and use save/open dialogs' most-recently-used data.

This commit is contained in:
Andrew Eikum 2010-07-23 13:13:04 -05:00 committed by Alexandre Julliard
parent bde57f0e64
commit 4b93548df1
2 changed files with 283 additions and 3 deletions

View File

@ -169,6 +169,13 @@ const char FileOpenDlgInfosStr[] = "FileOpenDlgInfos"; /* windows property descr
static const char LookInInfosStr[] = "LookInInfos"; /* LOOKIN combo box property */
static SIZE MemDialogSize = { 0, 0}; /* keep size of the (resizable) dialog */
static const WCHAR LastVisitedMRUW[] =
{'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\',
'W','i','n','d','o','w','s','\\','C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
'E','x','p','l','o','r','e','r','\\','C','o','m','D','l','g','3','2','\\',
'L','a','s','t','V','i','s','i','t','e','d','M','R','U',0};
static const WCHAR MRUListW[] = {'M','R','U','L','i','s','t',0};
/***********************************************************************
* Prototypes
*/
@ -209,6 +216,11 @@ static int FILEDLG95_LOOKIN_RemoveMostExpandedItem(HWND hwnd);
int FILEDLG95_LOOKIN_SelectItem(HWND hwnd,LPITEMIDLIST pidl);
static void FILEDLG95_LOOKIN_Clean(HWND hwnd);
/* Functions for dealing with the most-recently-used registry keys */
static void FILEDLG95_MRU_load_filename(LPWSTR stored_path);
static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret);
static void FILEDLG95_MRU_save_filename(LPCWSTR filename);
/* Miscellaneous tool functions */
static HRESULT GetName(LPSHELLFOLDER lpsf, LPITEMIDLIST pidl,DWORD dwFlags,LPWSTR lpstrFileName);
IShellFolder* GetShellFolderFromPidl(LPITEMIDLIST pidlAbs);
@ -1517,7 +1529,22 @@ static LRESULT FILEDLG95_InitControls(HWND hwnd)
SetDlgItemTextW(hwnd, IDC_FILENAME, fodInfos->filename);
}
/* 4. win98+ and win2000+ if any files of specified filter types in
/* 4. Win2000+: Recently used */
if (handledPath == FALSE && win2000plus) {
fodInfos->initdir = MemAlloc(MAX_PATH * sizeof(WCHAR));
fodInfos->initdir[0] = '\0';
FILEDLG95_MRU_load_filename(fodInfos->initdir);
if (fodInfos->initdir[0] && PathFileExistsW(fodInfos->initdir)){
handledPath = TRUE;
}else{
MemFree(fodInfos->initdir);
fodInfos->initdir = NULL;
}
}
/* 5. win98+ and win2000+ if any files of specified filter types in
current directory, use it */
if ( win98plus && handledPath == FALSE &&
fodInfos->filter && *fodInfos->filter) {
@ -1558,8 +1585,6 @@ static LRESULT FILEDLG95_InitControls(HWND hwnd)
}
}
/* 5. Win2000+: FIXME: Next, Recently used? Not sure how windows does this */
/* 6. Win98+ and 2000+: Use personal files dir, others use current dir */
if (handledPath == FALSE && (win2000plus || win98plus)) {
fodInfos->initdir = MemAlloc(MAX_PATH*sizeof(WCHAR));
@ -1953,6 +1978,199 @@ BOOL FILEDLG95_OnOpenMultipleFiles(HWND hwnd, LPWSTR lpstrFileList, UINT nFileCo
return EndDialog(hwnd,TRUE);
}
/* Returns the 'slot name' of the given module_name in the registry's
* most-recently-used list. This will be an ASCII value in the
* range ['a','z'). Returns zero on error.
*
* The slot's value in the registry has the form:
* module_name\0mru_path\0
*
* If stored_path is given, then stored_path will contain the path name
* stored in the registry's MRU list for the given module_name.
*
* If hkey_ret is given, then hkey_ret will be a handle to the registry's
* MRU list key for the given module_name.
*/
static WCHAR FILEDLG95_MRU_get_slot(LPCWSTR module_name, LPWSTR stored_path, PHKEY hkey_ret)
{
WCHAR mru_list[32], *cur_mru_slot;
BOOL taken[25] = {0};
DWORD mru_list_size = sizeof(mru_list), key_type = -1, i;
HKEY hkey_tmp, *hkey;
LONG ret;
if(hkey_ret)
hkey = hkey_ret;
else
hkey = &hkey_tmp;
if(stored_path)
*stored_path = '\0';
ret = RegCreateKeyW(HKEY_CURRENT_USER, LastVisitedMRUW, hkey);
if(ret){
WARN("Unable to create MRU key: %d\n", ret);
return 0;
}
ret = RegGetValueW(*hkey, NULL, MRUListW, RRF_RT_REG_SZ, &key_type,
(LPBYTE)mru_list, &mru_list_size);
if(ret || key_type != REG_SZ){
if(ret == ERROR_FILE_NOT_FOUND)
return 'a';
WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
RegCloseKey(*hkey);
return 0;
}
for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot){
WCHAR value_data[MAX_PATH], value_name[2] = {0};
DWORD value_data_size = sizeof(value_data);
*value_name = *cur_mru_slot;
ret = RegGetValueW(*hkey, NULL, value_name, RRF_RT_REG_BINARY,
&key_type, (LPBYTE)value_data, &value_data_size);
if(ret || key_type != REG_BINARY){
WARN("Error getting MRU slot data: type: %d, ret: %d\n", key_type, ret);
continue;
}
if(!strcmpiW(module_name, value_data)){
if(!hkey_ret)
RegCloseKey(*hkey);
if(stored_path)
lstrcpyW(stored_path, value_data + lstrlenW(value_data) + 1);
return *value_name;
}
}
if(!hkey_ret)
RegCloseKey(*hkey);
/* the module name isn't in the registry, so find the next open slot */
for(cur_mru_slot = mru_list; *cur_mru_slot; ++cur_mru_slot)
taken[*cur_mru_slot - 'a'] = TRUE;
for(i = 0; i < 25; ++i){
if(!taken[i])
return i + 'a';
}
/* all slots are taken, so return the last one in MRUList */
--cur_mru_slot;
return *cur_mru_slot;
}
/* save the given filename as most-recently-used path for this module */
static void FILEDLG95_MRU_save_filename(LPCWSTR filename)
{
WCHAR module_path[MAX_PATH], *module_name, slot, slot_name[2] = {0};
LONG ret;
HKEY hkey;
/* get the current executable's name */
if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path))){
WARN("GotModuleFileName failed: %d\n", GetLastError());
return;
}
module_name = strrchrW(module_path, '\\');
if(!module_name)
module_name = module_path;
else
module_name += 1;
slot = FILEDLG95_MRU_get_slot(module_name, NULL, &hkey);
if(!slot)
return;
*slot_name = slot;
{ /* update the slot's info */
WCHAR *path_ends, *final;
DWORD path_len, final_len;
/* use only the path segment of `filename' */
path_ends = strrchrW(filename, '\\');
path_len = path_ends - filename;
final_len = path_len + lstrlenW(module_name) + 2;
final = MemAlloc(final_len * sizeof(WCHAR));
if(!final)
return;
lstrcpyW(final, module_name);
memcpy(final + lstrlenW(final) + 1, filename, path_len * sizeof(WCHAR));
final[final_len-1] = '\0';
ret = RegSetValueExW(hkey, slot_name, 0, REG_BINARY, (LPBYTE)final,
final_len * sizeof(WCHAR));
if(ret){
WARN("Error saving MRU data to slot %s: %d\n", wine_dbgstr_w(slot_name), ret);
MemFree(final);
RegCloseKey(hkey);
return;
}
MemFree(final);
}
{ /* update MRUList value */
WCHAR old_mru_list[32], new_mru_list[32];
WCHAR *old_mru_slot, *new_mru_slot = new_mru_list;
DWORD mru_list_size = sizeof(old_mru_list), key_type;
ret = RegGetValueW(hkey, NULL, MRUListW, RRF_RT_ANY, &key_type,
(LPBYTE)old_mru_list, &mru_list_size);
if(ret || key_type != REG_SZ){
if(ret == ERROR_FILE_NOT_FOUND){
new_mru_list[0] = slot;
new_mru_list[1] = '\0';
}else{
WARN("Error getting MRUList data: type: %d, ret: %d\n", key_type, ret);
RegCloseKey(hkey);
return;
}
}else{
/* copy old list data over so that the new slot is at the start
* of the list */
*new_mru_slot++ = slot;
for(old_mru_slot = old_mru_list; *old_mru_slot; ++old_mru_slot){
if(*old_mru_slot != slot)
*new_mru_slot++ = *old_mru_slot;
}
*new_mru_slot = '\0';
}
ret = RegSetValueExW(hkey, MRUListW, 0, REG_SZ, (LPBYTE)new_mru_list,
(lstrlenW(new_mru_list) + 1) * sizeof(WCHAR));
if(ret){
WARN("Error saving MRUList data: %d\n", ret);
RegCloseKey(hkey);
return;
}
}
}
/* load the most-recently-used path for this module */
static void FILEDLG95_MRU_load_filename(LPWSTR stored_path)
{
WCHAR module_path[MAX_PATH], *module_name;
/* get the current executable's name */
if(!GetModuleFileNameW(GetModuleHandleW(NULL), module_path, sizeof(module_path))){
WARN("GotModuleFileName failed: %d\n", GetLastError());
return;
}
module_name = strrchrW(module_path, '\\');
if(!module_name)
module_name = module_path;
else
module_name += 1;
FILEDLG95_MRU_get_slot(module_name, stored_path, NULL);
TRACE("got MRU path: %s\n", wine_dbgstr_w(stored_path));
}
/***********************************************************************
* FILEDLG95_OnOpen
*
@ -2418,6 +2636,8 @@ BOOL FILEDLG95_OnOpen(HWND hwnd)
if ( !FILEDLG95_SendFileOK(hwnd, fodInfos) )
goto ret;
FILEDLG95_MRU_save_filename(lpstrPathAndFile);
TRACE("close\n");
FILEDLG95_Clean(hwnd);
ret = EndDialog(hwnd, TRUE);

View File

@ -985,6 +985,65 @@ static void test_resizable2(void)
#undef ISSIZABLE
}
static void test_mru(void)
{
ok_wndproc_testcase testcase = {0};
OPENFILENAMEW ofn = {sizeof(OPENFILENAMEW)};
const char *test_dir_nameA = "C:\\mru_test";
const WCHAR test_file_name[] =
{'t','e','s','t','.','t','x','t',0};
const WCHAR test_full_path[] =
{'C',':','\\','m','r','u','_','t','e','s','t','\\','t','e','s','t','.','t','x','t',0};
const WCHAR template1W[] =
{'t','e','m','p','l','a','t','e','1',0};
WCHAR filename_buf[MAX_PATH];
DWORD ret;
ofn.lpstrFile = filename_buf;
ofn.nMaxFile = sizeof(filename_buf);
ofn.lpTemplateName = template1W;
ofn.hInstance = GetModuleHandle(NULL);
ofn.Flags = OFN_ENABLEHOOK | OFN_EXPLORER | OFN_ENABLETEMPLATE | OFN_NOCHANGEDIR;
ofn.lCustData = (LPARAM)&testcase;
ofn.lpfnHook = (LPOFNHOOKPROC)test_ok_wndproc;
SetLastError(0xdeadbeef);
ret = CreateDirectoryA(test_dir_nameA, NULL);
ok(ret == TRUE, "CreateDirectoryA should have succeeded: %d\n", GetLastError());
/* "teach" comdlg32 about this directory */
lstrcpyW(filename_buf, test_full_path);
SetLastError(0xdeadbeef);
ret = GetOpenFileNameW(&ofn);
if(!ret && GetLastError() == ERROR_CALL_NOT_IMPLEMENTED){
win_skip("Platform doesn't implement GetOpenFileNameW\n");
RemoveDirectoryA(test_dir_nameA);
return;
}
ok(ret, "GetOpenFileNameW should have succeeded: %d\n", GetLastError());
ok(testcase.actclose, "Open File dialog should have closed.\n");
ret = CommDlgExtendedError();
ok(!ret, "CommDlgExtendedError returned %x\n", ret);
ok(!lstrcmpW(ofn.lpstrFile, test_full_path), "Expected to get %s, got %s\n",
wine_dbgstr_w(test_full_path), wine_dbgstr_w(ofn.lpstrFile));
/* get a filename without a full path. it should return the file in
* test_dir_name, not in the CWD */
lstrcpyW(filename_buf, test_file_name);
SetLastError(0xdeadbeef);
ret = GetOpenFileNameW(&ofn);
ok(ret, "GetOpenFileNameW should have succeeded: %d\n", GetLastError());
ok(testcase.actclose, "Open File dialog should have closed.\n");
ret = CommDlgExtendedError();
ok(!ret, "CommDlgExtendedError returned %x\n", ret);
if(lstrcmpW(ofn.lpstrFile, test_full_path) != 0)
win_skip("Platform doesn't save MRU data\n");
SetLastError(0xdeadbeef);
ret = RemoveDirectoryA(test_dir_nameA);
ok(ret == TRUE, "RemoveDirectoryA should have succeeded: %d\n", GetLastError());
}
START_TEST(filedlg)
{
test_DialogCancel();
@ -994,5 +1053,6 @@ START_TEST(filedlg)
test_resize();
test_ok();
test_getfolderpath();
test_mru();
if( resizesupported) test_resizable2();
}