Hi, This is the start of an implementation of property pages. The version page is pretty much complete, but the general page is only just starting. ChangeLog: - Allow user to view the properties of an file system object Rob
Index: wine/dlls/shell32/shlexec.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shlexec.c,v retrieving revision 1.16 diff -u -r1.16 shlexec.c --- wine/dlls/shell32/shlexec.c 3 Jan 2003 03:03:36 -0000 1.16 +++ wine/dlls/shell32/shlexec.c 11 Jan 2003 21:07:18 -0000 @@ -40,10 +40,12 @@ #include "shlobj.h" #include "shlwapi.h" #include "ddeml.h" +#include "winver.h" #include "wine/winbase16.h" #include "shell32_main.h" #include "undocshell.h" +#include "shresdef.h" #include "wine/debug.h" @@ -543,6 +545,8 @@ return (HINSTANCE)31; /* default - 'No association was found' */ } +HWND SHELL_ExecuteProperties(SHELLEXECUTEINFOA * sei); + /************************************************************************* * ShellExecuteExA32 [Internal] */ @@ -648,6 +652,12 @@ strcat(szApplicationName, szCommandline); } + if (!StrCmpIA(lpOperation, "properties")) + { + retval = (UINT)SHELL_ExecuteProperties(sei); + return TRUE; + } + retval = execfunc(szApplicationName, sei, FALSE); if (retval > 32) return TRUE; @@ -836,4 +846,310 @@ ShellExecuteExW (&sei); return sei.hInstApp; +} + +BOOL CALLBACK GeneralDialogProc(HWND hWndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam); +static HPROPSHEETPAGE CreateVersionPage(LPWSTR szPath); + +HWND SHELL_ExecuteProperties(SHELLEXECUTEINFOA * sei) +{ + PROPSHEETPAGEW psp; /* move to general create proc */ + PROPSHEETHEADERW psh; + HWND hWndProp; + LPWSTR wszPath; + WCHAR wszCaption[1024]; /* max len of wsprintf string*/ + WCHAR wszCaptionFormat[MAX_PATH + 1]; + HPROPSHEETPAGE hPsp[2]; /* FIXME: create dynamically */ + + wszPath = HeapAlloc(GetProcessHeap(), 0, (lstrlenA(sei->lpFile)+1)*sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, sei->lpFile, -1, wszPath, -1); + LoadStringW(shell32_hInstance, IDS_SHELL_PROPERTIES, wszCaptionFormat, MAX_PATH); + wsprintfW(wszCaption, wszCaptionFormat, wszPath); + + psp.dwSize = sizeof(PROPSHEETPAGEW); + psp.dwFlags = PSP_DEFAULT; + psp.hInstance = shell32_hInstance; + psp.u.pszTemplate = MAKEINTRESOURCEW(IDD_SHELL_GENERAL_SHEET); + psp.u2.pszIcon = NULL; + psp.pfnDlgProc = GeneralDialogProc; + psp.pszTitle = NULL; + psp.lParam = (LPARAM)wszPath; + psp.pfnCallback = NULL; + + hPsp[0] = CreatePropertySheetPageW(&psp); + hPsp[1] = CreateVersionPage(wszPath); + + psh.dwSize = sizeof(PROPSHEETHEADERW); + psh.dwFlags = PSH_DEFAULT; + psh.hwndParent = sei->hwnd; + psh.hInstance = shell32_hInstance; + psh.u.pszIcon = NULL; + psh.pszCaption = wszCaption; + psh.nPages = 2; + psh.u2.nStartPage = 0; + psh.u3.phpage = hPsp; + psh.pfnCallback = NULL; + + hWndProp = (HWND)PropertySheetW(&psh); + + return hWndProp; +} + +BOOL CALLBACK GeneralDialogProc(HWND hWndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + static PROPSHEETPAGEW * pPsp = NULL; + switch (uMsg) + { + case WM_INITDIALOG: + { + SHFILEINFOW shfi; + pPsp = (PROPSHEETPAGEW *)lParam; + SHGetFileInfoW((LPWSTR)pPsp->lParam, 0, &shfi, sizeof(SHFILEINFOW), SHGFI_DISPLAYNAME | SHGFI_ICON | SHGFI_LARGEICON | SHGFI_TYPENAME); + SendMessageW(GetDlgItem(hWndDialog, 12298), WM_SETTEXT, 0, (LPARAM)shfi.szDisplayName); + SendMessageW(GetDlgItem(hWndDialog, 12297), STM_SETIMAGE, IMAGE_ICON, (LPARAM)shfi.hIcon); + if (lstrlenW(shfi.szTypeName) == 0) + { + WCHAR buffer[1024]; /* sprintf limit */ + WCHAR format[128]; + LPWSTR extension = PathFindExtensionW((LPWSTR)pPsp->lParam); + if (extension) + { + LoadStringW(shell32_hInstance, IDS_SHELL_TYPEFORMAT, format, sizeof(format) / sizeof(*format) - 1); + wsprintfW(buffer, format, extension+1 /* remove '.' from front */); + SendMessageW(GetDlgItem(hWndDialog, 12296), WM_SETTEXT, 0, (LPARAM)buffer); + } + } + else + SendMessageW(GetDlgItem(hWndDialog, 12296), WM_SETTEXT, 0, (LPARAM)shfi.szTypeName); + + return TRUE; + } + case WM_DESTROY: + HeapFree(GetProcessHeap(), 0, (LPWSTR)pPsp->lParam); + pPsp->lParam = 0; + break; + default: + return FALSE; + } + return TRUE; +} + +/* copied from dlls/version/info.c */ +#define DWORD_ALIGN( base, ptr ) \ + ( (LPBYTE)(base) + ((((LPBYTE)(ptr) - (LPBYTE)(base)) + 3) & ~3) ) +#define VersionInfo32_Value( ver ) \ + DWORD_ALIGN( (ver), (ver)->szKey + lstrlenW((ver)->szKey) + 1 ) +#define VersionInfo32_Children( ver ) \ + ( VersionInfo32_Value( ver ) + \ + ( ( (ver)->wValueLength * \ + ((ver)->bText? 2 : 1) + 3 ) & ~3 ) ) + +typedef struct { + WORD wLength; + WORD wValueLength; + WORD wType; + WCHAR szKey[1]; +#if 0 /* not real members (see VersionInfo32_Value macro)*/ + WORD Padding[]; + WORD Value[]; +#endif +} VerString; + +typedef struct { + WORD wLength; + WORD wValueLength; + WORD bText; + WCHAR szKey[1]; +#if 0 + WORD Padding[]; + StringTable Children[]; +#endif +} StringFileInfo; + +UINT DWORD_ALIGN2(UINT value) +{ + if (value % 4 != 0) + value += 4 - value % 4; + return value; +} + +typedef struct +{ + LPWSTR szPath; + LPVOID pBlock; + LPWSTR pszLanguages; +} VerDlgData; + +static LRESULT VersionAddEntry(HWND hwndDlg, LPWSTR szKey, LPCWSTR szValue) +{ + UINT iItem = (UINT)SendMessageW(GetDlgItem(hwndDlg, 1005), LB_ADDSTRING, 0, (LPARAM)szKey); + return SendMessageW(GetDlgItem(hwndDlg, 1005), LB_SETITEMDATA, iItem, (LPARAM)szValue); +} + +/* Parse version resources and fill in controls on property sheet */ +static void VersionDialogInit(HWND hwnd, VerDlgData * dlgData) +{ + WCHAR SubBlock[MAX_PATH]; + WCHAR wcszTranslate[] = {'\\','V','a','r','F','i','l','e','I','n','f','o','\\', + 'T','r','a','n','s','l','a','t','i','o','n',0}; + WCHAR wcszSFI[] = {'\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',0}; + WCHAR wcszSlash[] = {'\\',0}; + WCHAR wcszComma[] = {',',' ',0}; + + /* FIXME: get from string resources */ + WCHAR wcszLanguage[] = {'L','a','n','g','u','a','g','e',0}; + WCHAR wcszLanguages[] = {'L','a','n','g','u','a','g','e','s',0}; + /*-- END FIXME --*/ + + WCHAR wcszVarFileInfo[] = {'V','a','r','F','i','l','e','I','n','f','o',0}; + WCHAR wcszFileDesc[] = {'F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0}; + WCHAR wcszFileVer[] = {'F','i','l','e','V','e','r','s','i','o','n',0}; + WCHAR wcszCopyright[] = {'L','e','g','a','l','C','o','p','y','r','i','g','h','t',0}; + + UINT nTranslations; /* number of languages present */ + UINT i; + UINT dwBytes; + VerString * pVerString; + StringFileInfo * pStringFileInfo; + + /* Structure used to store enumerated languages and code pages + * This is documented on MSDN with the VerQueryValue function, but + * is not defined anywhere in the official headers */ + struct LANGANDCODEPAGE + { + WORD wLanguage; + WORD wCodePage; + } *lpTranslate; + + /* Get the size of the resource block and fail if no version info available */ + DWORD blocksize = GetFileVersionInfoSizeW(dlgData->szPath, NULL); + dlgData->pBlock = HeapAlloc(GetProcessHeap(), 0, blocksize); + dlgData->pszLanguages = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (MAX_PATH + 1) * sizeof(WCHAR)); + + /* GetFileVersionInfo copies the resource block into pBlock for us to use in all the + * later functions */ + if (!blocksize || !GetFileVersionInfoW(dlgData->szPath, 0, blocksize, dlgData->pBlock)) + return; /* no file version available */ + + /* Read the list of languages (and code pages) */ + VerQueryValueW(dlgData->pBlock, + wcszTranslate, + (LPVOID*)&lpTranslate, + &nTranslations); + nTranslations /= sizeof(struct LANGANDCODEPAGE); + + + /* Get all the languages defined and convert them to a readable string */ + *dlgData->pszLanguages = '\0'; + for(i = 0; i < nTranslations; i++) + { + if (!VerQueryValueW(dlgData->pBlock, + wcszSFI, + (LPVOID *)&pStringFileInfo, + &dwBytes)) + return; + VerLanguageNameW(lpTranslate[i].wLanguage, SubBlock, MAX_PATH); + if (lstrlenW(dlgData->pszLanguages)) + { + StrCatW(dlgData->pszLanguages, wcszComma); + } + StrCatW(dlgData->pszLanguages, SubBlock); + } + VersionAddEntry(hwnd, nTranslations > 1 ? wcszLanguages : wcszLanguage, dlgData->pszLanguages); + + /* FIXME: we use first language defined. Is this right? */ + StrCpyW(SubBlock, wcszSFI); + StrCatW(SubBlock, wcszSlash); + StrCatW(SubBlock, pStringFileInfo->szKey); + if (!VerQueryValueW(dlgData->pBlock, + SubBlock, + (LPVOID *)&pVerString, + &dwBytes)) + return; + + dwBytes = (WORD)(int)VersionInfo32_Children(pStringFileInfo); /* get the real(!) size of the block from here */ + for (; (DWORD)pVerString < (DWORD)pStringFileInfo + dwBytes; pVerString = (VerString *)(((LPBYTE)pVerString)+DWORD_ALIGN2(pVerString->wLength))) + { + if (!StrCmpW(wcszVarFileInfo, pVerString->szKey)) /* FIXME: this is a hack to stop unwanted information appearing (dwBytes wrong???)*/ + break; + else + { + if (!StrCmpW(wcszFileDesc, pVerString->szKey)) + SendMessageW(GetDlgItem(hwnd, 1001), WM_SETTEXT, 0, (LPARAM)(pVerString->wValueLength > 0 ? VersionInfo32_Value(pVerString) : 0)); + else if (!StrCmpW(wcszFileVer, pVerString->szKey)) + SendMessageW(GetDlgItem(hwnd, 1000), WM_SETTEXT, 0, (LPARAM)(pVerString->wValueLength > 0 ? VersionInfo32_Value(pVerString) : 0)); + else if (!StrCmpW(wcszCopyright, pVerString->szKey)) + SendMessageW(GetDlgItem(hwnd, 1002), WM_SETTEXT, 0, (LPARAM)(pVerString->wValueLength > 0 ? VersionInfo32_Value(pVerString) : 0)); + else + { + /* FIXME: pretify the known key names */ + + /* Add entry to list, making sure we don't copy a load of rubbish to the text box + * from wValueLength == 0, but value not pointing to an empty string, just the start + * of the next item */ + if (isalpha((char)*pVerString->szKey)) /* FIXME: this is a hack to stop unwanted information appearing */ + VersionAddEntry(hwnd, pVerString->szKey, pVerString->wValueLength > 0 ? (LPWSTR)VersionInfo32_Value(pVerString) : 0); + } + } + } + return; +} + +static BOOL WINAPI VersionDialogProc(HWND hwndDialog, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + if (uMsg == WM_INITDIALOG) + { + VersionDialogInit(hwndDialog, (VerDlgData *)((LPPROPSHEETPAGEW)(lParam))->lParam); + SendMessageW(GetDlgItem(hwndDialog, 1005), LB_SETCURSEL, 0, 0); + SendMessageW(GetDlgItem(hwndDialog, 1004), WM_SETTEXT, 0, SendMessageW(GetDlgItem(hwndDialog, 1005), LB_GETITEMDATA, 0, 0)); + return TRUE; + } + if ((uMsg == WM_COMMAND) && (HIWORD(wParam) == LBN_SELCHANGE)) + { + UINT nCurSel = SendMessageW((HWND)lParam, LB_GETCURSEL, 0, 0); + SendMessageW(GetDlgItem(hwndDialog, 1004), WM_SETTEXT, 0, SendMessageW((HWND)lParam, LB_GETITEMDATA, nCurSel, 0)); + return TRUE; + } + return FALSE; +} + +/* Heap freeing calls put here as WM_DESTROY seems to be called when we don't want it + * in VersionDialogProc */ +static UINT CALLBACK VersionDialogCallback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGEW pPsp) +{ + if (uMsg == PSPCB_CREATE) + { + return TRUE; + } + if (uMsg == PSPCB_RELEASE) + { + VerDlgData * dlgData = (VerDlgData*)pPsp->lParam; + if (dlgData) + { + HeapFree(GetProcessHeap(), 0, dlgData->szPath); + HeapFree(GetProcessHeap(), 0, dlgData->pBlock); + HeapFree(GetProcessHeap(), 0, dlgData->pszLanguages); + HeapFree(GetProcessHeap(), 0, dlgData); + pPsp->lParam = 0; + } + } + return FALSE; +} + +/* Create the version property sheet */ +static HPROPSHEETPAGE CreateVersionPage(LPWSTR szPath) +{ + PROPSHEETPAGEW psp; + VerDlgData * pDlgData = (VerDlgData *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(VerDlgData)); + pDlgData->szPath = (LPWSTR)HeapAlloc(GetProcessHeap(), 0, (lstrlenW(szPath) + 1) * sizeof(WCHAR)); + StrCpyW(pDlgData->szPath, szPath); + + psp.dwSize = sizeof(PROPSHEETPAGEW); + psp.dwFlags = PSP_DEFAULT | PSP_USECALLBACK; + psp.hInstance = shell32_hInstance; + psp.u.pszTemplate = (LPCWSTR)400; + psp.lParam = (LPARAM)pDlgData; + psp.pfnDlgProc = VersionDialogProc; + psp.pfnCallback = VersionDialogCallback; + return CreatePropertySheetPageW(&psp); } Index: wine/dlls/shell32/shlfolder.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shlfolder.c,v retrieving revision 1.76 diff -u -r1.76 shlfolder.c --- wine/dlls/shell32/shlfolder.c 5 Dec 2002 20:33:08 -0000 1.76 +++ wine/dlls/shell32/shlfolder.c 11 Jan 2003 21:07:19 -0000 @@ -298,6 +298,9 @@ STRRET strTemp; LPITEMIDLIST pidlNext = ILGetNext (pidl); + // since we are dealing with ANSI strings anyway + strTemp.uType = STRRET_CSTR; + hr = IShellFolder_GetDisplayNameOf (psfChild, pidlNext, dwFlags, &strTemp); if (SUCCEEDED (hr)) { hr = StrRetToStrNA (szOut, dwOutLen, &strTemp, pidlNext); Index: wine/dlls/shell32/shres.rc =================================================================== RCS file: /home/wine/wine/dlls/shell32/shres.rc,v retrieving revision 1.27 diff -u -r1.27 shres.rc --- wine/dlls/shell32/shres.rc 19 Dec 2002 04:11:22 -0000 1.27 +++ wine/dlls/shell32/shres.rc 11 Jan 2003 21:07:21 -0000 @@ -156,6 +156,45 @@ IDS_OVERWRITEFILE_CAPTION "Confirm File OverWrite" } +/* resources for file property sheet */ +STRINGTABLE DISCARDABLE +{ + IDS_SHELL_PROPERTIES "%s properties" /* caption for properties dialog */ + IDS_SHELL_TYPEFORMAT "%s file" /* format of type name general property */ +} + +IDD_SHELL_GENERAL_SHEET DIALOG DISCARDABLE 0, 0, 227, 210 +STYLE DS_NOIDLEMSG | DS_3DLOOK | WS_CHILD | WS_CAPTION +CAPTION "General" +FONT 8,"Helv" +{ + ICON "", 12297, 7, 11, 18, 20, WS_VISIBLE + EDITTEXT 12298,48,15,78,12,ES_AUTOHSCROLL +/* LTEXT "Filename",12305,7,13,37,15 */ + LTEXT "<unknown type>", 12296,48,27,78,25 +} + +400 DIALOG DISCARDABLE 0, 0, 227, 210 +STYLE DS_NOIDLEMSG | DS_3DLOOK | WS_CHILD | WS_CAPTION +CAPTION "Version" +FONT 8, "MS Shell Dlg" +BEGIN + GROUPBOX "Other version information",1203,8,65,211,122 + EDITTEXT 1000,61,8,133,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT 1001,61,26,133,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + EDITTEXT 1002,61,44,133,13,ES_AUTOHSCROLL | ES_READONLY | NOT WS_BORDER + LTEXT "File version:",1200,12,8,49,9 + LTEXT "Description:",1201,12,25,49,9 + LTEXT "Copyright:",1202,12,44,49,9 + EDITTEXT 1004,113,90,97,89,ES_MULTILINE | ES_READONLY | + WS_VSCROLL + LTEXT "Item name:",1204,20,79,40,9 + LTEXT "Value:",1205,113,78,41,9 + LISTBOX 1005,16,90,91,89,LBS_SORT | LBS_NOINTEGRALHEIGHT | + WS_VSCROLL | WS_TABSTOP +END + + shv_accel ACCELERATORS BEGIN VK_F5, FCIDM_SHVIEW_REFRESH, VIRTKEY Index: wine/dlls/shell32/shresdef.h =================================================================== RCS file: /home/wine/wine/dlls/shell32/shresdef.h,v retrieving revision 1.10 diff -u -r1.10 shresdef.h --- wine/dlls/shell32/shresdef.h 19 Dec 2002 04:11:22 -0000 1.10 +++ wine/dlls/shell32/shresdef.h 11 Jan 2003 21:07:21 -0000 @@ -49,6 +49,10 @@ #define IDS_OVERWRITEFILE_CAPTION 36 #define IDS_OVERWRITEFILE_TEXT 37 +#define IDS_SHELL_PROPERTIES 38 +#define IDS_SHELL_TYPEFORMAT 39 +#define IDD_SHELL_GENERAL_SHEET 0x3744 + /* browse for folder dialog box */ #define IDD_STATUS 0x3743 #define IDD_TITLE 0x3742 Index: wine/dlls/shell32/shv_bg_cmenu.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shv_bg_cmenu.c,v retrieving revision 1.24 diff -u -r1.24 shv_bg_cmenu.c --- wine/dlls/shell32/shv_bg_cmenu.c 7 Jan 2003 20:36:24 -0000 1.24 +++ wine/dlls/shell32/shv_bg_cmenu.c 11 Jan 2003 21:07:21 -0000 @@ -94,7 +94,7 @@ TRACE("-- Interface: (%p)->(%p)\n",ppvObj,*ppvObj); return S_OK; } - TRACE("-- Interface: E_NOINTERFACE\n"); + ERR("-- Interface: %s - E_NOINTERFACE\n", debugstr_guid(riid)); return E_NOINTERFACE; } Index: wine/dlls/shell32/shv_item_cmenu.c =================================================================== RCS file: /home/wine/wine/dlls/shell32/shv_item_cmenu.c,v retrieving revision 1.15 diff -u -r1.15 shv_item_cmenu.c --- wine/dlls/shell32/shv_item_cmenu.c 7 Jan 2003 20:36:24 -0000 1.15 +++ wine/dlls/shell32/shv_item_cmenu.c 11 Jan 2003 21:07:21 -0000 @@ -220,8 +220,6 @@ TRACE("(%p)->(hmenu=%p indexmenu=%x cmdfirst=%x cmdlast=%x flags=%x )\n",This, hmenu, indexMenu, idCmdFirst, idCmdLast, uFlags); - if(!(CMF_DEFAULTONLY & uFlags)) - { if(uFlags & CMF_EXPLORE) { if(This->bAllValues) @@ -237,7 +235,7 @@ } else { - _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Select", MFS_ENABLED|MFS_DEFAULT); + _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_OPEN, MFT_STRING, "&Open", MFS_ENABLED|MFS_DEFAULT); } _InsertMenuItem(hmenu, indexMenu++, TRUE, 0, MFT_SEPARATOR, NULL, 0); _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_COPY, MFT_STRING, "&Copy", MFS_ENABLED); @@ -249,9 +247,9 @@ if(uFlags & CMF_CANRENAME) _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_RENAME, MFT_STRING, "&Rename", ISvItemCm_CanRenameItems(This) ? MFS_ENABLED : MFS_DISABLED); - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, (FCIDM_SHVIEWLAST)); - } - return MAKE_HRESULT(SEVERITY_SUCCESS, 0, 0); + _InsertMenuItem(hmenu, indexMenu++, TRUE, FCIDM_SHVIEW_PROPERTIES, MFT_STRING, "P&roperties", MFS_ENABLED); + + return MAKE_HRESULT(SEVERITY_SUCCESS, 0, FCIDM_SHVIEWLAST); } /************************************************************************** @@ -439,6 +437,9 @@ break; case FCIDM_SHVIEW_OPEN: DoOpenExplore(iface, lpcmi->hwnd, "open"); + break; + case FCIDM_SHVIEW_PROPERTIES: + DoOpenExplore(iface, lpcmi->hwnd, "properties"); break; case FCIDM_SHVIEW_RENAME: DoRename(iface, lpcmi->hwnd); Index: wine/dlls/shell32/Makefile.in =================================================================== RCS file: /home/wine/wine/dlls/shell32/Makefile.in,v retrieving revision 1.59 diff -u -r1.59 Makefile.in --- wine/dlls/shell32/Makefile.in 9 Jan 2003 00:03:53 -0000 1.59 +++ wine/dlls/shell32/Makefile.in 11 Jan 2003 21:07:12 -0000 @@ -5,7 +5,7 @@ VPATH = @srcdir@ MODULE = shell32.dll # fixme: avoid ole32.dll import -IMPORTS = ole32 shlwapi comctl32 user32 gdi32 advapi32 kernel32 +IMPORTS = ole32 shlwapi comctl32 user32 gdi32 advapi32 kernel32 version ALTNAMES = shell.dll EXTRALIBS = $(LIBUUID) $(LIBUNICODE)