From: Michael Müller michael@fds-team.de
Signed-off-by: Alistair Leslie-Hughes leslie_alistair@hotmail.com --- dlls/shell32/shell32.rc | 66 +++++++++ dlls/shell32/shlview_cmenu.c | 345 +++++++++++++++++++++++++++++++++++++++++-- dlls/shell32/shresdef.h | 28 ++++ 3 files changed, 425 insertions(+), 14 deletions(-)
diff --git a/dlls/shell32/shell32.rc b/dlls/shell32/shell32.rc index 6585416..f132d851 100644 --- a/dlls/shell32/shell32.rc +++ b/dlls/shell32/shell32.rc @@ -342,6 +342,72 @@ FONT 8, "MS Shell Dlg" PUSHBUTTON "&Browse...", IDC_RUNDLG_BROWSE, 180, 63, 50, 14, WS_TABSTOP }
+IDD_FOLDER_PROPERTIES DIALOGEX 0, 0, 240, 155 +STYLE DS_SHELLFONT | WS_CHILD | WS_CAPTION +CAPTION "General" +FONT 8, "MS Shell Dlg" +BEGIN + ICON "", IDC_FPROP_ICON, 10, 5, 32, 32, WS_VISIBLE + EDITTEXT IDC_FPROP_PATH, 70, 10, 160, 14, WS_TABSTOP + + LTEXT "", -1, 5, 30, 230, 1, SS_ETCHEDHORZ + LTEXT "File type:", IDC_FPROP_TYPE_LABEL, 10, 35, 60, 10 + EDITTEXT IDC_FPROP_TYPE, 70, 35, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + + LTEXT "", -1, 5, 50, 230, 1, SS_ETCHEDHORZ + LTEXT "Location:", IDC_FPROP_LOCATION_LABEL, 10, 55, 60, 10 + EDITTEXT IDC_FPROP_LOCATION, 70, 55, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Size:", IDC_FPROP_SIZE_LABEL, 10, 70, 60, 10 + EDITTEXT IDC_FPROP_SIZE, 70, 70, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + + LTEXT "", -1, 5, 85, 230, 1, SS_ETCHEDHORZ + LTEXT "Creation date:", IDC_FPROP_CREATED_LABEL, 10, 90, 60, 10 + EDITTEXT IDC_FPROP_CREATED, 70, 90, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + + LTEXT "", -1, 5, 105, 230, 1, SS_ETCHEDHORZ + LTEXT "Attributes:", IDC_FPROP_ATTRIB_LABEL, 10, 110, 60, 10 + AUTOCHECKBOX "&Read-only", IDC_FPROP_READONLY, 70, 110, 70, 10 + AUTOCHECKBOX "&Hidden", IDC_FPROP_HIDDEN, 70, 125, 70, 10 + AUTOCHECKBOX "&Archive", IDC_FPROP_ARCHIVE, 70, 140, 70, 10 +END + +IDD_FILE_PROPERTIES DIALOGEX 0, 0, 240, 200 +STYLE DS_SHELLFONT | WS_CHILD | WS_CAPTION +CAPTION "General" +FONT 8, "MS Shell Dlg" +BEGIN + ICON "", IDC_FPROP_ICON, 10, 5, 32, 32, WS_VISIBLE + EDITTEXT IDC_FPROP_PATH, 70, 10, 160, 14, WS_TABSTOP + + LTEXT "", -1, 5, 30, 230, 1, SS_ETCHEDHORZ + LTEXT "File type:", IDC_FPROP_TYPE_LABEL, 10, 35, 60, 10 + EDITTEXT IDC_FPROP_TYPE, 70, 35, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Open with:", IDC_FPROP_OPENWITH_LABEL, 10, 50, 60, 10 + ICON "", IDC_FPROP_PROG_ICON, 70, 50, 16, 16, WS_VISIBLE + EDITTEXT IDC_FPROP_PROG_NAME, 85, 50, 80, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + PUSHBUTTON "&Change...", IDC_FPROP_PROG_CHANGE, 170, 48, 60, 14, WS_CHILD | WS_TABSTOP + + LTEXT "", -1, 5, 65, 230, 1, SS_ETCHEDHORZ + LTEXT "Location:", IDC_FPROP_LOCATION_LABEL, 10, 70, 60, 10 + EDITTEXT IDC_FPROP_LOCATION, 70, 70, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Size:", IDC_FPROP_SIZE_LABEL, 10, 85, 60, 10 + EDITTEXT IDC_FPROP_SIZE, 70, 85, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + + LTEXT "", -1, 5, 100, 230, 1, SS_ETCHEDHORZ + LTEXT "Creation date:", IDC_FPROP_CREATED_LABEL, 10, 105, 60, 10 + EDITTEXT IDC_FPROP_CREATED, 70, 105, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Last modified:", IDC_FPROP_MODIFIED_LABEL, 10, 120, 60, 10 + EDITTEXT IDC_FPROP_MODIFIED, 70, 120, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + LTEXT "Last accessed:", IDC_FPROP_ACCESSED_LABEL, 10, 135, 60, 10 + EDITTEXT IDC_FPROP_ACCESSED, 70, 135, 160, 10, ES_READONLY | NOT WS_BORDER | NOT WS_TABSTOP + + LTEXT "", -1, 5, 150, 230, 1, SS_ETCHEDHORZ + LTEXT "Attributes:", IDC_FPROP_ATTRIB_LABEL, 10, 155, 60, 10 + AUTOCHECKBOX "&Read-only", IDC_FPROP_READONLY, 70, 155, 70, 10 + AUTOCHECKBOX "&Hidden", IDC_FPROP_HIDDEN, 70, 170, 70, 10 + AUTOCHECKBOX "&Archive", IDC_FPROP_ARCHIVE, 70, 185, 70, 10 +END + LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
/* @makedep: shell32.rgs */ diff --git a/dlls/shell32/shlview_cmenu.c b/dlls/shell32/shlview_cmenu.c index 9baaa27..06391bd 100644 --- a/dlls/shell32/shlview_cmenu.c +++ b/dlls/shell32/shlview_cmenu.c @@ -25,7 +25,6 @@ #define NONAMELESSUNION
#include "winerror.h" -#include "wine/debug.h"
#include "windef.h" #include "wingdi.h" @@ -39,6 +38,10 @@ #include "shellfolder.h"
#include "shresdef.h" +#include "shlwapi.h" + +#include "wine/heap.h" +#include "wine/debug.h"
WINE_DEFAULT_DEBUG_CHANNEL(shell);
@@ -300,6 +303,318 @@ static BOOL CALLBACK Properties_AddPropSheetCallback(HPROPSHEETPAGE hpage, LPARA return TRUE; }
+static BOOL format_date(FILETIME *time, WCHAR *buffer, DWORD size) +{ + FILETIME ft; + SYSTEMTIME st; + int ret; + + if (!FileTimeToLocalFileTime(time, &ft)) + return FALSE; + + if (!FileTimeToSystemTime(&ft, &st)) + return FALSE; + + ret = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_LONGDATE, &st, NULL, buffer, size); + if (ret) + { + buffer[ret - 1] = ' '; + ret = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &st, NULL, buffer + ret , size - ret); + } + return ret != 0; +} + +static BOOL get_program_description(WCHAR *path, WCHAR *buffer, DWORD size) +{ + static const WCHAR translationW[] = { + '\','V','a','r','F','i','l','e','I','n','f','o', + '\','T','r','a','n','s','l','a','t','i','o','n',0 + }; + static const WCHAR fileDescFmtW[] = { + '\','S','t','r','i','n','g','F','i','l','e','I','n','f','o', + '\','%','0','4','x','%','0','4','x', + '\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 + }; + WCHAR fileDescW[41], *desc; + DWORD versize, *lang; + UINT dlen, llen, i; + BOOL ret = FALSE; + PVOID data; + + versize = GetFileVersionInfoSizeW(path, NULL); + if (!versize) return FALSE; + + data = heap_alloc(versize); + if (!data) return FALSE; + + if (!GetFileVersionInfoW(path, 0, versize, data)) + goto out; + + if (!VerQueryValueW(data, translationW, (LPVOID *)&lang, &llen)) + goto out; + + for (i = 0; i < llen / sizeof(DWORD); i++) + { + sprintfW(fileDescW, fileDescFmtW, LOWORD(lang[i]), HIWORD(lang[i])); + if (VerQueryValueW(data, fileDescW, (LPVOID *)&desc, &dlen)) + { + if (dlen > size - 1) dlen = size - 1; + memcpy(buffer, desc, dlen * sizeof(WCHAR)); + buffer[dlen] = 0; + ret = TRUE; + break; + } + } + +out: + heap_free(data); + return ret; +} + +struct file_properties_info +{ + WCHAR path[MAX_PATH]; + WCHAR dir[MAX_PATH]; + WCHAR *filename; + DWORD attrib; +}; + +static void init_file_properties_dlg(HWND hwndDlg, struct file_properties_info *props) +{ + WCHAR buffer[MAX_PATH], buffer2[MAX_PATH]; + WIN32_FILE_ATTRIBUTE_DATA exinfo; + SHFILEINFOW shinfo; + + SetDlgItemTextW(hwndDlg, IDC_FPROP_PATH, props->filename); + SetDlgItemTextW(hwndDlg, IDC_FPROP_LOCATION, props->dir); + + if (SHGetFileInfoW(props->path, 0, &shinfo, sizeof(shinfo), SHGFI_TYPENAME|SHGFI_ICON)) + { + if (shinfo.hIcon) + { + SendDlgItemMessageW(hwndDlg, IDC_FPROP_ICON, STM_SETICON, (WPARAM)shinfo.hIcon, 0); + DestroyIcon(shinfo.hIcon); + } + if (shinfo.szTypeName[0]) + SetDlgItemTextW(hwndDlg, IDC_FPROP_TYPE, shinfo.szTypeName); + } + + if (!GetFileAttributesExW(props->path, GetFileExInfoStandard, &exinfo)) + return; + + if (format_date(&exinfo.ftCreationTime, buffer, ARRAY_SIZE(buffer))) + SetDlgItemTextW(hwndDlg, IDC_FPROP_CREATED, buffer); + + if (exinfo.dwFileAttributes & FILE_ATTRIBUTE_READONLY) + SendDlgItemMessageW(hwndDlg, IDC_FPROP_READONLY, BM_SETCHECK, BST_CHECKED, 0); + if (exinfo.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) + SendDlgItemMessageW(hwndDlg, IDC_FPROP_HIDDEN, BM_SETCHECK, BST_CHECKED, 0); + if (exinfo.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) + SendDlgItemMessageW(hwndDlg, IDC_FPROP_ARCHIVE, BM_SETCHECK, BST_CHECKED, 0); + + if (exinfo.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + static const WCHAR unknownW[] = {'(','u','n','k','n','o','w','n',')',0}; + SetDlgItemTextW(hwndDlg, IDC_FPROP_SIZE, unknownW); + + /* TODO: Implement counting for directories */ + return; + } + + /* Information about files only */ + StrFormatByteSizeW(((LONGLONG)exinfo.nFileSizeHigh << 32) | exinfo.nFileSizeLow, + buffer, ARRAY_SIZE(buffer)); + SetDlgItemTextW(hwndDlg, IDC_FPROP_SIZE, buffer); + + if (format_date(&exinfo.ftLastWriteTime, buffer, ARRAY_SIZE(buffer))) + SetDlgItemTextW(hwndDlg, IDC_FPROP_MODIFIED, buffer); + if (format_date(&exinfo.ftLastAccessTime, buffer, ARRAY_SIZE(buffer))) + SetDlgItemTextW(hwndDlg, IDC_FPROP_ACCESSED, buffer); + + if (FindExecutableW(props->path, NULL, buffer) <= (HINSTANCE)32) + return; + + /* Information about executables */ + if (SHGetFileInfoW(buffer, 0, &shinfo, sizeof(shinfo), SHGFI_ICON | SHGFI_SMALLICON) && shinfo.hIcon) + SendDlgItemMessageW(hwndDlg, IDC_FPROP_PROG_ICON, STM_SETICON, (WPARAM)shinfo.hIcon, 0); + + if (get_program_description(buffer, buffer2, ARRAY_SIZE(buffer2))) + SetDlgItemTextW(hwndDlg, IDC_FPROP_PROG_NAME, buffer2); + else + { + WCHAR *p = strrchrW(buffer, '\'); + SetDlgItemTextW(hwndDlg, IDC_FPROP_PROG_NAME, p ? ++p : buffer); + } +} + +static INT_PTR CALLBACK file_properties_proc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) +{ + switch (uMsg) + { + case WM_INITDIALOG: + { + LPPROPSHEETPAGEW page = (LPPROPSHEETPAGEW)lParam; + SetWindowLongPtrW(hwndDlg, DWLP_USER, (LONG_PTR)page->lParam); + init_file_properties_dlg(hwndDlg, (struct file_properties_info *)page->lParam); + break; + } + + case WM_COMMAND: + if (LOWORD(wParam) == IDC_FPROP_PROG_CHANGE) + { + /* TODO: Implement file association dialog */ + MessageBoxA(hwndDlg, "Not implemented yet.", "Error", MB_OK | MB_ICONEXCLAMATION); + } + else if (LOWORD(wParam) == IDC_FPROP_READONLY || + LOWORD(wParam) == IDC_FPROP_HIDDEN || + LOWORD(wParam) == IDC_FPROP_ARCHIVE) + { + SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + } + else if (LOWORD(wParam) == IDC_FPROP_PATH && HIWORD(wParam) == EN_CHANGE) + { + SendMessageW(GetParent(hwndDlg), PSM_CHANGED, (WPARAM)hwndDlg, 0); + } + break; + + case WM_NOTIFY: + { + LPPSHNOTIFY notify = (LPPSHNOTIFY)lParam; + if (notify->hdr.code == PSN_APPLY) + { + struct file_properties_info *props = (struct file_properties_info *)GetWindowLongPtrW(hwndDlg, DWLP_USER); + WCHAR newname[MAX_PATH], newpath[MAX_PATH]; + DWORD attributes; + + attributes = GetFileAttributesW(props->path); + if (attributes != INVALID_FILE_ATTRIBUTES) + { + attributes &= ~(FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_ARCHIVE); + + if (SendDlgItemMessageW(hwndDlg, IDC_FPROP_READONLY, BM_GETCHECK, 0, 0) == BST_CHECKED) + attributes |= FILE_ATTRIBUTE_READONLY; + if (SendDlgItemMessageW(hwndDlg, IDC_FPROP_HIDDEN, BM_GETCHECK, 0, 0) == BST_CHECKED) + attributes |= FILE_ATTRIBUTE_HIDDEN; + if (SendDlgItemMessageW(hwndDlg, IDC_FPROP_ARCHIVE, BM_GETCHECK, 0, 0) == BST_CHECKED) + attributes |= FILE_ATTRIBUTE_ARCHIVE; + + if (!SetFileAttributesW(props->path, attributes)) + ERR("failed to update file attributes of %s\n", debugstr_w(props->path)); + } + + /* Update filename it it was changed */ + if (GetDlgItemTextW(hwndDlg, IDC_FPROP_PATH, newname, ARRAY_SIZE(newname)) && + strcmpW(props->filename, newname) && + strlenW(props->dir) + strlenW(newname) + 2 < ARRAY_SIZE(newpath)) + { + static const WCHAR slash[] = {'\', 0}; + strcpyW(newpath, props->dir); + strcatW(newpath, slash); + strcatW(newpath, newname); + + if (!MoveFileW(props->path, newpath)) + ERR("failed to move file %s to %s\n", debugstr_w(props->path), debugstr_w(newpath)); + else + { + WCHAR *p; + strcpyW(props->path, newpath); + strcpyW(props->dir, newpath); + if ((p = strrchrW(props->dir, '\'))) + { + *p = 0; + props->filename = p + 1; + } + else + props->filename = props->dir; + SetDlgItemTextW(hwndDlg, IDC_FPROP_LOCATION, props->dir); + } + } + + return TRUE; + } + } + break; + + default: + break; + } + return FALSE; +} + +static UINT CALLBACK file_properties_callback(HWND hwnd, UINT uMsg, LPPROPSHEETPAGEW page) +{ + struct file_properties_info *props = (struct file_properties_info *)page->lParam; + if (uMsg == PSPCB_RELEASE) + { + heap_free(props); + } + return 1; +} + +static void init_file_properties_pages(IDataObject *dataobject, LPFNADDPROPSHEETPAGE lpfnAddPage, LPARAM lParam) +{ + struct file_properties_info *props; + HPROPSHEETPAGE general_page; + PROPSHEETPAGEW propsheet; + FORMATETC format; + STGMEDIUM stgm; + HRESULT hr; + WCHAR *p; + + props = heap_alloc(sizeof(*props)); + if (!props) return; + + format.cfFormat = CF_HDROP; + format.ptd = NULL; + format.dwAspect = DVASPECT_CONTENT; + format.lindex = -1; + format.tymed = TYMED_HGLOBAL; + + hr = IDataObject_GetData(dataobject, &format, &stgm); + if (FAILED(hr)) goto error; + + if (!DragQueryFileW((HDROP)stgm.u.hGlobal, 0, props->path, ARRAY_SIZE(props->path))) + { + ReleaseStgMedium(&stgm); + goto error; + } + + ReleaseStgMedium(&stgm); + + props->attrib = GetFileAttributesW(props->path); + if (props->attrib == INVALID_FILE_ATTRIBUTES) + goto error; + + strcpyW(props->dir, props->path); + if ((p = strrchrW(props->dir, '\'))) + { + *p = 0; + props->filename = p + 1; + } + else + props->filename = props->dir; + + memset(&propsheet, 0, sizeof(propsheet)); + propsheet.dwSize = sizeof(propsheet); + propsheet.dwFlags = PSP_DEFAULT | PSP_USECALLBACK; + propsheet.hInstance = shell32_hInstance; + if (props->attrib & FILE_ATTRIBUTE_DIRECTORY) + propsheet.u.pszTemplate = (LPWSTR)MAKEINTRESOURCE(IDD_FOLDER_PROPERTIES); + else + propsheet.u.pszTemplate = (LPWSTR)MAKEINTRESOURCE(IDD_FILE_PROPERTIES); + propsheet.pfnDlgProc = file_properties_proc; + propsheet.pfnCallback = file_properties_callback; + propsheet.lParam = (LPARAM)props; + + general_page = CreatePropertySheetPageW(&propsheet); + if (general_page) + lpfnAddPage(general_page, lParam); + return; + +error: + heap_free(props); +} + #define MAX_PROP_PAGES 99
static void DoOpenProperties(ContextMenu *This, HWND hwnd) @@ -381,19 +696,21 @@ static void DoOpenProperties(ContextMenu *This, HWND hwnd)
if (SUCCEEDED(ret)) { - hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletype, MAX_PROP_PAGES - psh.nPages, lpDo); - if (hpsxa != NULL) - { - SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh); - SHDestroyPropSheetExtArray(hpsxa); - } - hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletypeAll, MAX_PROP_PAGES - psh.nPages, lpDo); - if (hpsxa != NULL) - { - SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh); - SHDestroyPropSheetExtArray(hpsxa); - } - IDataObject_Release(lpDo); + init_file_properties_pages(lpDo, Properties_AddPropSheetCallback, (LPARAM)&psh); + + hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletype, MAX_PROP_PAGES - psh.nPages, lpDo); + if (hpsxa != NULL) + { + SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh); + SHDestroyPropSheetExtArray(hpsxa); + } + hpsxa = SHCreatePropSheetExtArrayEx(HKEY_CLASSES_ROOT, wszFiletypeAll, MAX_PROP_PAGES - psh.nPages, lpDo); + if (hpsxa != NULL) + { + SHAddFromPropSheetExtArray(hpsxa, Properties_AddPropSheetCallback, (LPARAM)&psh); + SHDestroyPropSheetExtArray(hpsxa); + } + IDataObject_Release(lpDo); }
if (psh.nPages) diff --git a/dlls/shell32/shresdef.h b/dlls/shell32/shresdef.h index 183a75e..1518de5 100644 --- a/dlls/shell32/shresdef.h +++ b/dlls/shell32/shresdef.h @@ -152,6 +152,10 @@ /* Note: this string is referenced from the registry*/ #define IDS_RECYCLEBIN_FOLDER_NAME 8964
+/* Properties dialog */ +#define IDD_FILE_PROPERTIES 8 +#define IDD_FOLDER_PROPERTIES 9 + #define IDD_ICON 0x4300 #define IDD_MESSAGE 0x4301
@@ -232,6 +236,30 @@ FIXME: Need to add them, but for now just let them use the same: searching.avi #define IDC_RUNDLG_EDITPATH 12298 #define IDC_RUNDLG_LABEL 12305
+/* file property dialog */ +#define IDC_FPROP_ICON 13000 +#define IDC_FPROP_PATH 13001 +#define IDC_FPROP_TYPE_LABEL 13002 +#define IDC_FPROP_TYPE 13003 +#define IDC_FPROP_OPENWITH_LABEL 13004 +#define IDC_FPROP_PROG_ICON 13005 +#define IDC_FPROP_PROG_NAME 13006 +#define IDC_FPROP_PROG_CHANGE 13007 +#define IDC_FPROP_LOCATION_LABEL 13008 +#define IDC_FPROP_LOCATION 13009 +#define IDC_FPROP_SIZE_LABEL 13010 +#define IDC_FPROP_SIZE 13011 +#define IDC_FPROP_CREATED_LABEL 13012 +#define IDC_FPROP_CREATED 13013 +#define IDC_FPROP_MODIFIED_LABEL 13014 +#define IDC_FPROP_MODIFIED 13015 +#define IDC_FPROP_ACCESSED_LABEL 13016 +#define IDC_FPROP_ACCESSED 13017 +#define IDC_FPROP_ATTRIB_LABEL 13018 +#define IDC_FPROP_READONLY 13019 +#define IDC_FPROP_HIDDEN 13020 +#define IDC_FPROP_ARCHIVE 13021 + /* bitmaps */ /* explorer toolbar icons * FIXME: images are hacky and should be re-drawn; also dark and light bitmaps are same for now