/* * Open With Context Menu extension * * Copyright 2007 Johannes Anderwald * Copyright 2016-2018 Katayama Hirofumi MZ * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #include "precomp.h" WINE_DEFAULT_DEBUG_CHANNEL (fprop); /// Folder Options: /// CLASSKEY = HKEY_CLASSES_ROOT\CLSID\{6DFD7C5C-2451-11d3-A299-00C04F8EF6AF} /// DefaultIcon = %SystemRoot%\system32\SHELL32.dll,-210 /// Verbs: Open / RunAs /// Cmd: rundll32.exe shell32.dll,Options_RunDLL 0 /// ShellFolder Attributes: 0x0 typedef struct { WCHAR FileExtension[30]; WCHAR FileDescription[100]; WCHAR ClassKey[MAX_PATH]; DWORD EditFlags; } FOLDER_FILE_TYPE_ENTRY, *PFOLDER_FILE_TYPE_ENTRY; // uniquely-defined icon entry for Advanced Settings typedef struct ADVANCED_ICON { WCHAR szPath[MAX_PATH]; UINT nIconIndex; } ADVANCED_ICON; // predefined icon IDs (See CreateTreeImageList function below) #define I_CHECKED 0 #define I_UNCHECKED 1 #define I_CHECKED_DISABLED 2 #define I_UNCHECKED_DISABLED 3 #define I_RADIO_CHECKED 4 #define I_RADIO_UNCHECKED 5 #define I_RADIO_CHECKED_DISABLED 6 #define I_RADIO_UNCHECKED_DISABLED 7 #define PREDEFINED_ICON_COUNT 8 // definition of icon stock static ADVANCED_ICON * s_AdvancedIcons = NULL; static INT s_AdvancedIconCount = 0; static HIMAGELIST s_hImageList = NULL; static INT Advanced_FindIcon(LPCWSTR pszPath, UINT nIconIndex) { for (INT i = PREDEFINED_ICON_COUNT; i < s_AdvancedIconCount; ++i) { ADVANCED_ICON *pIcon = &s_AdvancedIcons[i]; if (pIcon->nIconIndex == nIconIndex && lstrcmpiW(pIcon->szPath, pszPath) == 0) { return i; // icon ID } } return -1; // not found } static INT Advanced_AddIcon(LPCWSTR pszPath, UINT nIconIndex) { ADVANCED_ICON *pAllocated; // return the ID if already existed INT nIconID = Advanced_FindIcon(pszPath, nIconIndex); if (nIconID != -1) return nIconID; // already exists // extract a small icon HICON hIconSmall = NULL; ExtractIconExW(pszPath, nIconIndex, NULL, &hIconSmall, 1); if (hIconSmall == NULL) return -1; // failure // resize s_AdvancedIcons size_t Size = (s_AdvancedIconCount + 1) * sizeof(ADVANCED_ICON); pAllocated = (ADVANCED_ICON *)realloc(s_AdvancedIcons, Size); if (pAllocated == NULL) return -1; // failure else s_AdvancedIcons = pAllocated; // save icon information ADVANCED_ICON *pIcon = &s_AdvancedIcons[s_AdvancedIconCount]; lstrcpynW(pIcon->szPath, pszPath, _countof(pIcon->szPath)); pIcon->nIconIndex = nIconIndex; // add the icon to the image list ImageList_AddIcon(s_hImageList, hIconSmall); // increment the counter nIconID = s_AdvancedIconCount; ++s_AdvancedIconCount; DestroyIcon(hIconSmall); return nIconID; // newly-added icon ID } // types of Advanced Setting entry typedef enum ADVANCED_ENTRY_TYPE { AETYPE_GROUP, AETYPE_CHECKBOX, AETYPE_RADIO, } ADVANCED_ENTRY_TYPE; // an entry info of Advanced Settings typedef struct ADVANCED_ENTRY { DWORD dwID; // entry ID DWORD dwParentID; // parent entry ID DWORD dwResourceID; // resource ID WCHAR szKeyName[64]; // entry key name DWORD dwType; // ADVANCED_ENTRY_TYPE WCHAR szText[MAX_PATH]; // text INT nIconID; // icon ID (See ADVANCED_ICON) HKEY hkeyRoot; // registry root key WCHAR szRegPath[MAX_PATH]; // registry path WCHAR szValueName[64]; // registry value name DWORD dwCheckedValue; // checked value DWORD dwUncheckedValue; // unchecked value DWORD dwDefaultValue; // defalut value BOOL bHasUncheckedValue; // If FALSE, UncheckedValue is invalid HTREEITEM hItem; // for TreeView BOOL bGrayed; // disabled? BOOL bChecked; // checked? } ADVANCED_ENTRY, *PADVANCED_ENTRY; // definition of advanced entries static ADVANCED_ENTRY * s_Advanced = NULL; static INT s_AdvancedCount = 0; static HBITMAP Create24BppBitmap(HDC hDC, INT cx, INT cy) { BITMAPINFO bi; LPVOID pvBits; ZeroMemory(&bi, sizeof(bi)); bi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); bi.bmiHeader.biWidth = cx; bi.bmiHeader.biHeight = cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = 24; bi.bmiHeader.biCompression = BI_RGB; HBITMAP hbm = CreateDIBSection(hDC, &bi, DIB_RGB_COLORS, &pvBits, NULL, 0); return hbm; } static HBITMAP CreateCheckImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE) { INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON); INT cySmallIcon = GetSystemMetrics(SM_CYSMICON); HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon); if (hbm == NULL) return NULL; // failure RECT Rect, BoxRect; SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon); BoxRect = Rect; InflateRect(&BoxRect, -1, -1); HGDIOBJ hbmOld = SelectObject(hDC, hbm); { UINT uState = DFCS_BUTTONCHECK | DFCS_FLAT | DFCS_MONO; if (bCheck) uState |= DFCS_CHECKED; if (!bEnabled) uState |= DFCS_INACTIVE; DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState); } SelectObject(hDC, hbmOld); return hbm; // success } static HBITMAP CreateCheckMask(HDC hDC) { INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON); INT cySmallIcon = GetSystemMetrics(SM_CYSMICON); HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL); if (hbm == NULL) return NULL; // failure RECT Rect, BoxRect; SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon); BoxRect = Rect; InflateRect(&BoxRect, -1, -1); HGDIOBJ hbmOld = SelectObject(hDC, hbm); { FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH))); FillRect(hDC, &BoxRect, HBRUSH(GetStockObject(BLACK_BRUSH))); } SelectObject(hDC, hbmOld); return hbm; // success } static HBITMAP CreateRadioImage(HDC hDC, BOOL bCheck, BOOL bEnabled = TRUE) { INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON); INT cySmallIcon = GetSystemMetrics(SM_CYSMICON); HBITMAP hbm = Create24BppBitmap(hDC, cxSmallIcon, cySmallIcon); if (hbm == NULL) return NULL; // failure RECT Rect, BoxRect; SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon); BoxRect = Rect; InflateRect(&BoxRect, -1, -1); HGDIOBJ hbmOld = SelectObject(hDC, hbm); { UINT uState = DFCS_BUTTONRADIOIMAGE | DFCS_FLAT | DFCS_MONO; if (bCheck) uState |= DFCS_CHECKED; if (!bEnabled) uState |= DFCS_INACTIVE; DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState); } SelectObject(hDC, hbmOld); return hbm; // success } static HBITMAP CreateRadioMask(HDC hDC) { INT cxSmallIcon = GetSystemMetrics(SM_CXSMICON); INT cySmallIcon = GetSystemMetrics(SM_CYSMICON); HBITMAP hbm = CreateBitmap(cxSmallIcon, cySmallIcon, 1, 1, NULL); if (hbm == NULL) return NULL; // failure RECT Rect, BoxRect; SetRect(&Rect, 0, 0, cxSmallIcon, cySmallIcon); BoxRect = Rect; InflateRect(&BoxRect, -1, -1); HGDIOBJ hbmOld = SelectObject(hDC, hbm); { FillRect(hDC, &Rect, HBRUSH(GetStockObject(WHITE_BRUSH))); UINT uState = DFCS_BUTTONRADIOMASK | DFCS_FLAT | DFCS_MONO; DrawFrameControl(hDC, &BoxRect, DFC_BUTTON, uState); } SelectObject(hDC, hbmOld); return hbm; // success } static HIMAGELIST CreateTreeImageList(VOID) { HIMAGELIST hImageList; hImageList = ImageList_Create(16, 16, ILC_COLOR24 | ILC_MASK, 9, 1); if (hImageList == NULL) return NULL; // failure // free if existed if (s_AdvancedIcons) { free(s_AdvancedIcons); s_AdvancedIcons = NULL; } s_AdvancedIconCount = 0; // allocate now ADVANCED_ICON *pAllocated; size_t Size = PREDEFINED_ICON_COUNT * sizeof(ADVANCED_ICON); pAllocated = (ADVANCED_ICON *)calloc(1, Size); if (pAllocated == NULL) return NULL; // failure s_AdvancedIconCount = PREDEFINED_ICON_COUNT; s_AdvancedIcons = pAllocated; // add the predefined icons HDC hDC = CreateCompatibleDC(NULL); HBITMAP hbmMask = CreateCheckMask(hDC); HBITMAP hbmChecked, hbmUnchecked; hbmChecked = CreateCheckImage(hDC, TRUE); ImageList_Add(hImageList, hbmChecked, hbmMask); DeleteObject(hbmChecked); hbmUnchecked = CreateCheckImage(hDC, FALSE); ImageList_Add(hImageList, hbmUnchecked, hbmMask); DeleteObject(hbmUnchecked); hbmChecked = CreateCheckImage(hDC, TRUE, FALSE); ImageList_Add(hImageList, hbmChecked, hbmMask); DeleteObject(hbmChecked); hbmUnchecked = CreateCheckImage(hDC, FALSE, FALSE); ImageList_Add(hImageList, hbmUnchecked, hbmMask); DeleteObject(hbmUnchecked); DeleteObject(hbmMask); hbmMask = CreateRadioMask(hDC); hbmChecked = CreateRadioImage(hDC, TRUE); ImageList_Add(hImageList, hbmChecked, hbmMask); DeleteObject(hbmChecked); hbmUnchecked = CreateRadioImage(hDC, FALSE); ImageList_Add(hImageList, hbmUnchecked, hbmMask); DeleteObject(hbmUnchecked); hbmChecked = CreateRadioImage(hDC, TRUE, FALSE); ImageList_Add(hImageList, hbmChecked, hbmMask); DeleteObject(hbmChecked); hbmUnchecked = CreateRadioImage(hDC, FALSE, FALSE); ImageList_Add(hImageList, hbmUnchecked, hbmMask); DeleteObject(hbmUnchecked); DeleteObject(hbmMask); return hImageList; } static ADVANCED_ENTRY * Advanced_GetItem(DWORD dwID) { if (dwID == DWORD(-1)) return NULL; for (INT i = 0; i < s_AdvancedCount; ++i) { ADVANCED_ENTRY *pEntry = &s_Advanced[i]; if (pEntry->dwID == dwID) return pEntry; } return NULL; // failure } static INT Advanced_GetImage(ADVANCED_ENTRY *pEntry) { switch (pEntry->dwType) { case AETYPE_GROUP: return pEntry->nIconID; case AETYPE_CHECKBOX: if (pEntry->bGrayed) { if (pEntry->bChecked) return I_CHECKED_DISABLED; else return I_UNCHECKED_DISABLED; } else { if (pEntry->bChecked) return I_CHECKED; else return I_UNCHECKED; } case AETYPE_RADIO: if (pEntry->bGrayed) { if (pEntry->bChecked) return I_RADIO_CHECKED_DISABLED; else return I_RADIO_UNCHECKED_DISABLED; } else { if (pEntry->bChecked) return I_RADIO_CHECKED; else return I_RADIO_UNCHECKED; } } return -1; // failure } static VOID Advanced_InsertEntry(HWND hwndTreeView, ADVANCED_ENTRY *pEntry) { ADVANCED_ENTRY *pParent = Advanced_GetItem(pEntry->dwParentID); HTREEITEM hParent = TVI_ROOT; if (pParent) hParent = pParent->hItem; TV_INSERTSTRUCT Insertion; ZeroMemory(&Insertion, sizeof(Insertion)); Insertion.hParent = hParent; Insertion.hInsertAfter = TVI_LAST; Insertion.item.mask = TVIF_TEXT | TVIF_IMAGE | TVIF_SELECTEDIMAGE | TVIF_PARAM; Insertion.item.pszText = pEntry->szText; INT iImage = Advanced_GetImage(pEntry); Insertion.item.iImage = Insertion.item.iSelectedImage = iImage; Insertion.item.lParam = pEntry->dwID; pEntry->hItem = TreeView_InsertItem(hwndTreeView, &Insertion); } static VOID Advanced_InsertAll(HWND hwndTreeView) { TreeView_DeleteAllItems(hwndTreeView); // insert the entries ADVANCED_ENTRY *pEntry; for (INT i = 0; i < s_AdvancedCount; ++i) { pEntry = &s_Advanced[i]; Advanced_InsertEntry(hwndTreeView, pEntry); } // expand all for (INT i = 0; i < s_AdvancedCount; ++i) { pEntry = &s_Advanced[i]; if (pEntry->dwType == AETYPE_GROUP) { TreeView_Expand(hwndTreeView, pEntry->hItem, TVE_EXPAND); } } } static BOOL Advanced_LoadTree(HKEY hKey, LPCWSTR pszKeyName, DWORD dwParentID) { DWORD dwIndex; WCHAR szKeyName[64], szText[MAX_PATH], *pch; DWORD Size, Value; ADVANCED_ENTRY *pAllocated; // resize s_Advanced Size = (s_AdvancedCount + 1) * sizeof(ADVANCED_ENTRY); pAllocated = (ADVANCED_ENTRY *)realloc(s_Advanced, Size); if (pAllocated == NULL) return FALSE; // failure else s_Advanced = pAllocated; ADVANCED_ENTRY *pEntry = &s_Advanced[s_AdvancedCount]; // dwID, dwParentID, szKeyName pEntry->dwID = s_AdvancedCount; pEntry->dwParentID = dwParentID; lstrcpynW(pEntry->szKeyName, pszKeyName, _countof(pEntry->szKeyName)); // Text, ResourceID pEntry->szText[0] = 0; pEntry->dwResourceID = 0; szText[0] = 0; Size = sizeof(szText); RegQueryValueExW(hKey, L"Text", NULL, NULL, LPBYTE(szText), &Size); if (szText[0] == L'@') { pch = wcsrchr(szText, L','); if (pch) { *pch = 0; dwIndex = abs(_wtoi(pch + 1)); pEntry->dwResourceID = dwIndex; } HINSTANCE hInst = LoadLibraryW(&szText[1]); LoadStringW(hInst, dwIndex, szText, _countof(szText)); FreeLibrary(hInst); } else { pEntry->dwResourceID = DWORD(-1); } lstrcpynW(pEntry->szText, szText, _countof(pEntry->szText)); // Type szText[0] = 0; RegQueryValueExW(hKey, L"Type", NULL, NULL, LPBYTE(szText), &Size); if (lstrcmpiW(szText, L"checkbox") == 0) pEntry->dwType = AETYPE_CHECKBOX; else if (lstrcmpiW(szText, L"radio") == 0) pEntry->dwType = AETYPE_RADIO; else if (lstrcmpiW(szText, L"group") == 0) pEntry->dwType = AETYPE_GROUP; else return FALSE; // failure pEntry->nIconID = -1; if (pEntry->dwType == AETYPE_GROUP) { // Bitmap (Icon) UINT nIconIndex = 0; Size = sizeof(szText); szText[0] = 0; RegQueryValueExW(hKey, L"Bitmap", NULL, NULL, LPBYTE(szText), &Size); WCHAR szExpanded[MAX_PATH]; ExpandEnvironmentStringsW(szText, szExpanded, _countof(szExpanded)); pch = wcsrchr(szExpanded, L','); if (pch) { *pch = 0; nIconIndex = abs(_wtoi(pch + 1)); } pEntry->nIconID = Advanced_AddIcon(szExpanded, nIconIndex); } if (pEntry->dwType == AETYPE_GROUP) { pEntry->hkeyRoot = NULL; pEntry->szRegPath[0] = 0; pEntry->szValueName[0] = 0; pEntry->dwCheckedValue = 0; pEntry->bHasUncheckedValue = FALSE; pEntry->dwUncheckedValue = 0; pEntry->dwDefaultValue = 0; pEntry->hItem = NULL; pEntry->bGrayed = FALSE; pEntry->bChecked = FALSE; } else { // HKeyRoot Value = DWORD(HKEY_CURRENT_USER); Size = sizeof(Value); RegQueryValueExW(hKey, L"HKeyRoot", NULL, NULL, LPBYTE(&Value), &Size); pEntry->hkeyRoot = HKEY(Value); // RegPath pEntry->szRegPath[0] = 0; Size = sizeof(szText); RegQueryValueExW(hKey, L"RegPath", NULL, NULL, LPBYTE(szText), &Size); lstrcpynW(pEntry->szRegPath, szText, _countof(pEntry->szRegPath)); // ValueName pEntry->szValueName[0] = 0; Size = sizeof(szText); RegQueryValueExW(hKey, L"ValueName", NULL, NULL, LPBYTE(szText), &Size); lstrcpynW(pEntry->szValueName, szText, _countof(pEntry->szValueName)); // CheckedValue Size = sizeof(Value); Value = 0x00000001; RegQueryValueExW(hKey, L"CheckedValue", NULL, NULL, LPBYTE(&Value), &Size); pEntry->dwCheckedValue = Value; // UncheckedValue Size = sizeof(Value); Value = 0x00000000; pEntry->bHasUncheckedValue = TRUE; if (RegQueryValueExW(hKey, L"UncheckedValue", NULL, NULL, LPBYTE(&Value), &Size) != ERROR_SUCCESS) { pEntry->bHasUncheckedValue = FALSE; } pEntry->dwUncheckedValue = Value; // DefaultValue Size = sizeof(Value); Value = 0x00000001; RegQueryValueExW(hKey, L"DefaultValue", NULL, NULL, LPBYTE(&Value), &Size); pEntry->dwDefaultValue = Value; // hItem pEntry->hItem = NULL; // bGrayed, bChecked HKEY hkeyTarget; Value = pEntry->dwDefaultValue; pEntry->bGrayed = TRUE; if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0, KEY_READ, &hkeyTarget) == ERROR_SUCCESS) { Size = sizeof(Value); if (RegQueryValueExW(hkeyTarget, pEntry->szValueName, NULL, NULL, LPBYTE(&Value), &Size) == ERROR_SUCCESS) { pEntry->bGrayed = FALSE; } RegCloseKey(hkeyTarget); } pEntry->bChecked = (Value == pEntry->dwCheckedValue); } // Grayed (ReactOS extension) Size = sizeof(Value); Value = FALSE; RegQueryValueExW(hKey, L"Grayed", NULL, NULL, LPBYTE(&Value), &Size); if (!pEntry->bGrayed) pEntry->bGrayed = Value; BOOL bIsGroup = (pEntry->dwType == AETYPE_GROUP); dwParentID = pEntry->dwID; ++s_AdvancedCount; if (!bIsGroup) return TRUE; // success // load the children dwIndex = 0; while (RegEnumKeyW(hKey, dwIndex, szKeyName, _countof(szKeyName)) == ERROR_SUCCESS) { HKEY hkeyChild; if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ, &hkeyChild) != ERROR_SUCCESS) { ++dwIndex; continue; // failure } Advanced_LoadTree(hkeyChild, szKeyName, dwParentID); RegCloseKey(hkeyChild); ++dwIndex; } return TRUE; // success } static BOOL Advanced_LoadAll(VOID) { static const WCHAR s_szAdvanced[] = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced"; // free if already existed if (s_Advanced) { free(s_Advanced); s_Advanced = NULL; } s_AdvancedCount = 0; HKEY hKey; if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, s_szAdvanced, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { return FALSE; // failure } // load the children WCHAR szKeyName[64]; DWORD dwIndex = 0; while (RegEnumKeyW(hKey, dwIndex, szKeyName, _countof(szKeyName)) == ERROR_SUCCESS) { HKEY hkeyChild; if (RegOpenKeyExW(hKey, szKeyName, 0, KEY_READ, &hkeyChild) != ERROR_SUCCESS) { ++dwIndex; continue; // failure } Advanced_LoadTree(hkeyChild, szKeyName, DWORD(-1)); RegCloseKey(hkeyChild); ++dwIndex; } RegCloseKey(hKey); return TRUE; // success } static int Advanced_Compare(const void *x, const void *y) { ADVANCED_ENTRY *pEntry1 = (ADVANCED_ENTRY *)x; ADVANCED_ENTRY *pEntry2 = (ADVANCED_ENTRY *)y; DWORD dwParentID1 = pEntry1->dwParentID; DWORD dwParentID2 = pEntry2->dwParentID; if (dwParentID1 == dwParentID2) return lstrcmpi(pEntry1->szText, pEntry2->szText); DWORD i, m, n; const UINT MAX_DEPTH = 32; ADVANCED_ENTRY *pArray1[MAX_DEPTH]; ADVANCED_ENTRY *pArray2[MAX_DEPTH]; // Make ancestor lists for (i = m = n = 0; i < MAX_DEPTH; ++i) { ADVANCED_ENTRY *pParent1 = Advanced_GetItem(dwParentID1); ADVANCED_ENTRY *pParent2 = Advanced_GetItem(dwParentID2); if (!pParent1 && !pParent2) break; if (pParent1) { pArray1[m++] = pParent1; dwParentID1 = pParent1->dwParentID; } if (pParent2) { pArray2[n++] = pParent2; dwParentID2 = pParent2->dwParentID; } } UINT k = min(m, n); for (i = 0; i < k; ++i) { INT nCompare = lstrcmpi(pArray1[m - i - 1]->szText, pArray2[n - i - 1]->szText); if (nCompare < 0) return -1; if (nCompare > 0) return 1; } if (m < n) return -1; if (m > n) return 1; return lstrcmpi(pEntry1->szText, pEntry2->szText); } static VOID Advanced_SortAll(VOID) { qsort(s_Advanced, s_AdvancedCount, sizeof(ADVANCED_ENTRY), Advanced_Compare); } EXTERN_C HPSXA WINAPI SHCreatePropSheetExtArrayEx(HKEY hKey, LPCWSTR pszSubKey, UINT max_iface, IDataObject *pDataObj); static VOID UpdateGeneralIcons(HWND hDlg) { HWND hwndTaskIcon, hwndFolderIcon, hwndClickIcon; HICON hTaskIcon = NULL, hFolderIcon = NULL, hClickIcon = NULL; LPTSTR lpTaskIconName = NULL, lpFolderIconName = NULL, lpClickIconName = NULL; // show task setting icon if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_COMMONTASKS) == BST_CHECKED) lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_SHOW_COMMON_TASKS); else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_CLASSICFOLDERS) == BST_CHECKED) lpTaskIconName = MAKEINTRESOURCE(IDI_SHELL_CLASSIC_FOLDERS); if (lpTaskIconName) { hTaskIcon = (HICON)LoadImage(shell32_hInstance, lpTaskIconName, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); if (hTaskIcon) { hwndTaskIcon = GetDlgItem(hDlg, IDC_FOLDER_OPTIONS_TASKICON); if (hwndTaskIcon) { SendMessage(hwndTaskIcon, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hTaskIcon); } } } // show Folder setting icons if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SAMEWINDOW) == BST_CHECKED) lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_SOME_WINDOW); else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_OWNWINDOW) == BST_CHECKED) lpFolderIconName = MAKEINTRESOURCE(IDI_SHELL_OPEN_IN_NEW_WINDOW); if (lpFolderIconName) { hFolderIcon = (HICON)LoadImage(shell32_hInstance, lpFolderIconName, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); if (hFolderIcon) { hwndFolderIcon = GetDlgItem(hDlg, IDC_FOLDER_OPTIONS_FOLDERICON); if (hwndFolderIcon) { SendMessage(hwndFolderIcon, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hFolderIcon); } } } // Show click setting icon if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_SINGLECLICK) == BST_CHECKED) lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_SINGLE_CLICK_TO_OPEN); else if(IsDlgButtonChecked(hDlg, IDC_FOLDER_OPTIONS_DOUBLECLICK) == BST_CHECKED) lpClickIconName = MAKEINTRESOURCE(IDI_SHELL_DOUBLE_CLICK_TO_OPEN); if (lpClickIconName) { hClickIcon = (HICON)LoadImage(shell32_hInstance, lpClickIconName, IMAGE_ICON, 0, 0, LR_DEFAULTCOLOR); if (hClickIcon) { hwndClickIcon = GetDlgItem(hDlg, IDC_FOLDER_OPTIONS_CLICKICON); if (hwndClickIcon) { SendMessage(hwndClickIcon, STM_SETIMAGE, IMAGE_ICON, (LPARAM)hClickIcon); } } } // Clean up if(hTaskIcon) DeleteObject(hTaskIcon); if(hFolderIcon) DeleteObject(hFolderIcon); if(hClickIcon) DeleteObject(hClickIcon); return; } INT_PTR CALLBACK FolderOptionsGeneralDlg( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) { switch(uMsg) { case WM_INITDIALOG: // FIXME break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_FOLDER_OPTIONS_COMMONTASKS: case IDC_FOLDER_OPTIONS_CLASSICFOLDERS: case IDC_FOLDER_OPTIONS_SAMEWINDOW: case IDC_FOLDER_OPTIONS_OWNWINDOW: case IDC_FOLDER_OPTIONS_SINGLECLICK: case IDC_FOLDER_OPTIONS_DOUBLECLICK: if (HIWORD(wParam) == BN_CLICKED) { UpdateGeneralIcons(hwndDlg); /* Enable the 'Apply' button */ PropSheet_Changed(GetParent(hwndDlg), hwndDlg); } break; } break; case WM_NOTIFY: { LPNMHDR pnmh = (LPNMHDR)lParam; switch (pnmh->code) { case PSN_SETACTIVE: break; case PSN_APPLY: break; } break; } case WM_DESTROY: break; default: return FALSE; } return FALSE; } static BOOL ViewDlg_OnInitDialog(HWND hwndDlg) { HWND hwndTreeView = GetDlgItem(hwndDlg, 14003); s_hImageList = CreateTreeImageList(); TreeView_SetImageList(hwndTreeView, s_hImageList, TVSIL_NORMAL); Advanced_LoadAll(); Advanced_SortAll(); Advanced_InsertAll(hwndTreeView); return TRUE; // set focus } static BOOL ViewDlg_ToggleCheckItem(HWND hwndDlg, HTREEITEM hItem) { HWND hwndTreeView = GetDlgItem(hwndDlg, 14003); // get the item TV_ITEM Item; INT i; ZeroMemory(&Item, sizeof(Item)); Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_PARAM; Item.hItem = hItem; if (!TreeView_GetItem(hwndTreeView, &Item)) return FALSE; // no such item ADVANCED_ENTRY *pEntry = Advanced_GetItem(Item.lParam); if (pEntry == NULL) return FALSE; // no such item if (pEntry->bGrayed) return FALSE; // disabled // toggle check mark Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; switch (pEntry->dwType) { case AETYPE_CHECKBOX: pEntry->bChecked = !pEntry->bChecked; break; case AETYPE_RADIO: // reset all the entries of the same parent for (i = 0; i < s_AdvancedCount; ++i) { ADVANCED_ENTRY *pEntry2 = &s_Advanced[i]; if (pEntry->dwParentID == pEntry2->dwParentID) { pEntry2->bChecked = FALSE; Item.hItem = pEntry2->hItem; INT iImage = Advanced_GetImage(pEntry2); Item.iImage = Item.iSelectedImage = iImage; TreeView_SetItem(hwndTreeView, &Item); } } pEntry->bChecked = TRUE; break; default: return FALSE; // failure } Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry); Item.hItem = hItem; TreeView_SetItem(hwndTreeView, &Item); // redraw the item RECT rcItem; TreeView_GetItemRect(hwndTreeView, hItem, &rcItem, FALSE); InvalidateRect(hwndTreeView, &rcItem, TRUE); return TRUE; // success } static VOID ViewDlg_OnTreeViewClick(HWND hwndDlg) { HWND hwndTreeView = GetDlgItem(hwndDlg, 14003); // do hit test to get the clicked item TV_HITTESTINFO HitTest; ZeroMemory(&HitTest, sizeof(HitTest)); DWORD dwPos = GetMessagePos(); HitTest.pt.x = LOWORD(dwPos); HitTest.pt.y = HIWORD(dwPos); ScreenToClient(hwndTreeView, &HitTest.pt); HTREEITEM hItem = TreeView_HitTest(hwndTreeView, &HitTest); // toggle the check mark if possible if (ViewDlg_ToggleCheckItem(hwndDlg, hItem)) { // property sheet was changed PropSheet_Changed(GetParent(hwndDlg), hwndDlg); } } static void ViewDlg_OnTreeViewKeyDown(HWND hwndDlg, TV_KEYDOWN *KeyDown) { HWND hwndTreeView = GetDlgItem(hwndDlg, 14003); if (KeyDown->wVKey == VK_SPACE) { // [Space] key was pressed HTREEITEM hItem = TreeView_GetSelection(hwndTreeView); if (ViewDlg_ToggleCheckItem(hwndDlg, hItem)) { PropSheet_Changed(GetParent(hwndDlg), hwndDlg); } } } static INT_PTR ViewDlg_OnTreeCustomDraw(HWND hwndDlg, NMTVCUSTOMDRAW *Draw) { NMCUSTOMDRAW& nmcd = Draw->nmcd; switch (nmcd.dwDrawStage) { case CDDS_PREPAINT: return CDRF_NOTIFYITEMDRAW; // for CDDS_ITEMPREPAINT case CDDS_ITEMPREPAINT: if (!(nmcd.uItemState & CDIS_SELECTED)) // not selected { LPARAM lParam = nmcd.lItemlParam; ADVANCED_ENTRY *pEntry = Advanced_GetItem(lParam); if (pEntry && pEntry->bGrayed) // disabled { // draw as grayed Draw->clrText = GetSysColor(COLOR_GRAYTEXT); Draw->clrTextBk = GetSysColor(COLOR_WINDOW); return CDRF_NEWFONT; } } break; default: break; } return CDRF_DODEFAULT; } static VOID Advanced_RestoreDefaults(HWND hwndDlg) { HWND hwndTreeView = GetDlgItem(hwndDlg, 14003); for (INT i = 0; i < s_AdvancedCount; ++i) { // ignore if the type is group ADVANCED_ENTRY *pEntry = &s_Advanced[i]; if (pEntry->dwType == AETYPE_GROUP) continue; // set default value on registry HKEY hKey; if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0, KEY_WRITE, &hKey) != ERROR_SUCCESS) { continue; } RegSetValueExW(hKey, pEntry->szValueName, 0, REG_DWORD, LPBYTE(pEntry->dwDefaultValue), sizeof(DWORD)); RegCloseKey(hKey); // update check status pEntry->bChecked = (pEntry->dwCheckedValue == pEntry->dwDefaultValue); // update the image TV_ITEM Item; ZeroMemory(&Item, sizeof(Item)); Item.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE; Item.hItem = pEntry->hItem; Item.iImage = Item.iSelectedImage = Advanced_GetImage(pEntry); TreeView_SetItem(hwndTreeView, &Item); } PropSheet_Changed(GetParent(hwndDlg), hwndDlg); } /* FIXME: These macros should not be defined here */ #ifndef SSF_SHOWSUPERHIDDEN #define SSF_SHOWSUPERHIDDEN 0x00040000 #endif #ifndef SSF_SEPPROCESS #define SSF_SEPPROCESS 0x00080000 #endif static VOID ScanAdvancedSettings(SHELLSTATE *pSS, DWORD *pdwMask) { for (INT i = 0; i < s_AdvancedCount; ++i) { const ADVANCED_ENTRY *pEntry = &s_Advanced[i]; if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed) continue; BOOL bChecked = pEntry->bChecked; // FIXME: Add more items if (lstrcmpiW(pEntry->szKeyName, L"SuperHidden") == 0) { pSS->fShowSuperHidden = !bChecked ? 1 : 0; *pdwMask |= SSF_SHOWSUPERHIDDEN; continue; } if (lstrcmpiW(pEntry->szKeyName, L"DesktopProcess") == 0) { pSS->fSepProcess = bChecked ? 1 : 0; *pdwMask |= SSF_SEPPROCESS; continue; } if (lstrcmpiW(pEntry->szKeyName, L"SHOWALL") == 0) { pSS->fShowAllObjects = !bChecked ? 1 : 0; *pdwMask |= SSF_SHOWALLOBJECTS; continue; } if (lstrcmpiW(pEntry->szKeyName, L"HideFileExt") == 0) { pSS->fShowExtensions = !bChecked ? 1 : 0; *pdwMask |= SSF_SHOWEXTENSIONS; continue; } if (lstrcmpiW(pEntry->szKeyName, L"ShowCompColor") == 0) { pSS->fShowCompColor = bChecked ? 1 : 0; *pdwMask |= SSF_SHOWCOMPCOLOR; continue; } if (lstrcmpiW(pEntry->szKeyName, L"ShowInfoTip") == 0) { pSS->fShowInfoTip = bChecked ? 1 : 0; *pdwMask |= SSF_SHOWINFOTIP; continue; } } } extern "C" VOID WINAPI SHGetSetSettings(LPSHELLSTATE lpss, DWORD dwMask, BOOL bSet); static BOOL CALLBACK RefreshBrowsersCallback (HWND hWnd, LPARAM msg) { WCHAR ClassName[100]; if (GetClassName(hWnd, ClassName, 100)) { if (!wcscmp(ClassName, L"Progman") || !wcscmp(ClassName, L"CabinetWClass") || !wcscmp(ClassName, L"ExploreWClass")) { PostMessage(hWnd, WM_COMMAND, FCIDM_DESKBROWSER_REFRESH, 0); } } return TRUE; } static VOID ViewDlg_Apply(HWND hwndDlg) { for (INT i = 0; i < s_AdvancedCount; ++i) { // ignore the entry if the type is group or the entry is grayed ADVANCED_ENTRY *pEntry = &s_Advanced[i]; if (pEntry->dwType == AETYPE_GROUP || pEntry->bGrayed) continue; // open the registry key HKEY hkeyTarget; if (RegOpenKeyExW(HKEY(pEntry->hkeyRoot), pEntry->szRegPath, 0, KEY_WRITE, &hkeyTarget) != ERROR_SUCCESS) { continue; } // checked or unchecked? DWORD dwValue, dwSize; if (pEntry->bChecked) { dwValue = pEntry->dwCheckedValue; } else { if (pEntry->bHasUncheckedValue) { dwValue = pEntry->dwUncheckedValue; } else { // there is no unchecked value RegCloseKey(hkeyTarget); continue; // ignore } } // set the value dwSize = sizeof(dwValue); RegSetValueExW(hkeyTarget, pEntry->szValueName, 0, REG_DWORD, LPBYTE(&dwValue), dwSize); // close now RegCloseKey(hkeyTarget); } // scan advanced settings for user's settings DWORD dwMask = 0; SHELLSTATE ShellState; ZeroMemory(&ShellState, sizeof(ShellState)); ScanAdvancedSettings(&ShellState, &dwMask); // update user's settings SHGetSetSettings(&ShellState, dwMask, TRUE); // notify all SendMessage(HWND_BROADCAST, WM_WININICHANGE, 0, 0); EnumWindows(RefreshBrowsersCallback, NULL); } INT_PTR CALLBACK FolderOptionsViewDlg( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { INT_PTR Result; NMTVCUSTOMDRAW *Draw; switch(uMsg) { case WM_INITDIALOG: return ViewDlg_OnInitDialog(hwndDlg); case WM_COMMAND: switch (LOWORD(wParam)) { case 14004: // Restore Defaults Advanced_RestoreDefaults(hwndDlg); break; } break; case WM_NOTIFY: switch (LPNMHDR(lParam)->code) { case NM_CLICK: // clicked on treeview ViewDlg_OnTreeViewClick(hwndDlg); break; case NM_CUSTOMDRAW: // custom draw (for graying) Draw = (NMTVCUSTOMDRAW *)lParam; Result = ViewDlg_OnTreeCustomDraw(hwndDlg, Draw); SetWindowLongPtr(hwndDlg, DWLP_MSGRESULT, Result); return Result; case TVN_KEYDOWN: // key is down ViewDlg_OnTreeViewKeyDown(hwndDlg, (TV_KEYDOWN *)lParam); break; case PSN_APPLY: // [Apply] is clicked ViewDlg_Apply(hwndDlg); break; default: break; } break; } return FALSE; } static VOID InitializeFileTypesListCtrlColumns(HWND hDlgCtrl) { RECT clientRect; LVCOLUMNW col; WCHAR szName[50]; DWORD dwStyle; int columnSize = 140; if (!LoadStringW(shell32_hInstance, IDS_COLUMN_EXTENSION, szName, sizeof(szName) / sizeof(WCHAR))) { /* default to english */ wcscpy(szName, L"Extensions"); } /* make sure its null terminated */ szName[(sizeof(szName)/sizeof(WCHAR))-1] = 0; GetClientRect(hDlgCtrl, &clientRect); ZeroMemory(&col, sizeof(LV_COLUMN)); columnSize = 140; //FIXME col.iSubItem = 0; col.mask = LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT; col.fmt = LVCFMT_FIXED_WIDTH; col.cx = columnSize | LVCFMT_LEFT; col.cchTextMax = wcslen(szName); col.pszText = szName; (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 0, (LPARAM)&col); if (!LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szName, sizeof(szName) / sizeof(WCHAR))) { /* default to english */ wcscpy(szName, L"File Types"); ERR("Failed to load localized string!\n"); } col.iSubItem = 1; col.cx = clientRect.right - clientRect.left - columnSize; col.cchTextMax = wcslen(szName); col.pszText = szName; (void)SendMessageW(hDlgCtrl, LVM_INSERTCOLUMNW, 1, (LPARAM)&col); /* set full select style */ dwStyle = (DWORD) SendMessage(hDlgCtrl, LVM_GETEXTENDEDLISTVIEWSTYLE, 0, 0); dwStyle = dwStyle | LVS_EX_FULLROWSELECT; SendMessage(hDlgCtrl, LVM_SETEXTENDEDLISTVIEWSTYLE, 0, dwStyle); } INT FindItem(HWND hDlgCtrl, WCHAR * ItemName) { LVFINDINFOW findInfo; ZeroMemory(&findInfo, sizeof(LVFINDINFOW)); findInfo.flags = LVFI_STRING; findInfo.psz = ItemName; return ListView_FindItem(hDlgCtrl, 0, &findInfo); } static VOID InsertFileType(HWND hDlgCtrl, WCHAR * szName, PINT iItem, WCHAR * szFile) { PFOLDER_FILE_TYPE_ENTRY Entry; HKEY hKey; LVITEMW lvItem; DWORD dwSize; DWORD dwType; if (szName[0] != L'.') { /* FIXME handle URL protocol handlers */ return; } /* allocate file type entry */ Entry = (PFOLDER_FILE_TYPE_ENTRY)HeapAlloc(GetProcessHeap(), 0, sizeof(FOLDER_FILE_TYPE_ENTRY)); if (!Entry) return; /* open key */ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, szName, 0, KEY_READ, &hKey) != ERROR_SUCCESS) { HeapFree(GetProcessHeap(), 0, Entry); return; } /* FIXME check for duplicates */ /* query for the default key */ dwSize = sizeof(Entry->ClassKey); if (RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->ClassKey, &dwSize) != ERROR_SUCCESS) { /* no link available */ Entry->ClassKey[0] = 0; } if (Entry->ClassKey[0]) { HKEY hTemp; /* try open linked key */ if (RegOpenKeyExW(HKEY_CLASSES_ROOT, Entry->ClassKey, 0, KEY_READ, &hTemp) == ERROR_SUCCESS) { /* use linked key */ RegCloseKey(hKey); hKey = hTemp; } } /* read friendly type name */ if (RegLoadMUIStringW(hKey, L"FriendlyTypeName", Entry->FileDescription, sizeof(Entry->FileDescription), NULL, 0, NULL) != ERROR_SUCCESS) { /* read file description */ dwSize = sizeof(Entry->FileDescription); Entry->FileDescription[0] = 0; /* read default key */ RegQueryValueExW(hKey, NULL, NULL, NULL, (LPBYTE)Entry->FileDescription, &dwSize); } /* Read the EditFlags value */ Entry->EditFlags = 0; if (!RegQueryValueExW(hKey, L"EditFlags", NULL, &dwType, NULL, &dwSize)) { if ((dwType == REG_DWORD || dwType == REG_BINARY) && dwSize == sizeof(DWORD)) RegQueryValueExW(hKey, L"EditFlags", NULL, NULL, (LPBYTE)&Entry->EditFlags, &dwSize); } /* close key */ RegCloseKey(hKey); /* Do not add excluded entries */ if (Entry->EditFlags & 0x00000001) //FTA_Exclude { HeapFree(GetProcessHeap(), 0, Entry); return; } /* convert extension to upper case */ wcscpy(Entry->FileExtension, szName); _wcsupr(Entry->FileExtension); if (!Entry->FileDescription[0]) { /* construct default 'FileExtensionFile' by formatting the uppercase extension with IDS_FILE_EXT_TYPE, outputting something like a l18n 'INI File' */ StringCchPrintf(Entry->FileDescription, _countof(Entry->FileDescription), szFile, &Entry->FileExtension[1]); } ZeroMemory(&lvItem, sizeof(LVITEMW)); lvItem.mask = LVIF_TEXT | LVIF_PARAM; lvItem.iSubItem = 0; lvItem.pszText = &Entry->FileExtension[1]; lvItem.iItem = *iItem; lvItem.lParam = (LPARAM)Entry; (void)SendMessageW(hDlgCtrl, LVM_INSERTITEMW, 0, (LPARAM)&lvItem); ZeroMemory(&lvItem, sizeof(LVITEMW)); lvItem.mask = LVIF_TEXT; lvItem.pszText = Entry->FileDescription; lvItem.iItem = *iItem; lvItem.iSubItem = 1; ListView_SetItem(hDlgCtrl, &lvItem); (*iItem)++; } static int CALLBACK ListViewCompareProc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort) { PFOLDER_FILE_TYPE_ENTRY Entry1, Entry2; int x; Entry1 = (PFOLDER_FILE_TYPE_ENTRY)lParam1; Entry2 = (PFOLDER_FILE_TYPE_ENTRY)lParam2; x = wcsicmp(Entry1->FileExtension, Entry2->FileExtension); if (x != 0) return x; return wcsicmp(Entry1->FileDescription, Entry2->FileDescription); } static PFOLDER_FILE_TYPE_ENTRY InitializeFileTypesListCtrl(HWND hwndDlg) { HWND hDlgCtrl; DWORD dwIndex = 0; WCHAR szName[50]; WCHAR szFile[100]; DWORD dwName; LVITEMW lvItem; INT iItem = 0; hDlgCtrl = GetDlgItem(hwndDlg, 14000); InitializeFileTypesListCtrlColumns(hDlgCtrl); szFile[0] = 0; if (!LoadStringW(shell32_hInstance, IDS_FILE_EXT_TYPE, szFile, _countof(szFile))) { /* default to english */ wcscpy(szFile, L"%s File"); } szFile[(_countof(szFile)) - 1] = 0; dwName = _countof(szName); while (RegEnumKeyExW(HKEY_CLASSES_ROOT, dwIndex++, szName, &dwName, NULL, NULL, NULL, NULL) == ERROR_SUCCESS) { InsertFileType(hDlgCtrl, szName, &iItem, szFile); dwName = _countof(szName); } /* Leave if the list is empty */ if (iItem == 0) return NULL; /* sort list */ ListView_SortItems(hDlgCtrl, ListViewCompareProc, NULL); /* select first item */ ZeroMemory(&lvItem, sizeof(LVITEMW)); lvItem.mask = LVIF_STATE; lvItem.stateMask = (UINT)-1; lvItem.state = LVIS_FOCUSED | LVIS_SELECTED; lvItem.iItem = 0; ListView_SetItem(hDlgCtrl, &lvItem); lvItem.mask = LVIF_PARAM; ListView_GetItem(hDlgCtrl, &lvItem); return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam; } static PFOLDER_FILE_TYPE_ENTRY FindSelectedItem( HWND hDlgCtrl) { UINT Count, Index; LVITEMW lvItem; Count = ListView_GetItemCount(hDlgCtrl); for (Index = 0; Index < Count; Index++) { ZeroMemory(&lvItem, sizeof(LVITEM)); lvItem.mask = LVIF_PARAM | LVIF_STATE; lvItem.iItem = Index; lvItem.stateMask = (UINT) - 1; if (ListView_GetItem(hDlgCtrl, &lvItem)) { if (lvItem.state & LVIS_SELECTED) return (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam; } } return NULL; } struct NEWEXT_DIALOG { HWND hwndDlg; HWND hwndLV; RECT rcDlg; BOOL bAdvanced; INT dy; WCHAR szExt[16]; WCHAR szFileType[64]; }; static VOID NewExtDlg_OnAdvanced(HWND hwndDlg, NEWEXT_DIALOG *pNewExt) { // If "Advanced" button was clicked, then we shrink or expand the dialog. WCHAR szText[64]; RECT rc, rc1, rc2; GetWindowRect(hwndDlg, &rc); rc.bottom = rc.top + (pNewExt->rcDlg.bottom - pNewExt->rcDlg.top); GetWindowRect(GetDlgItem(hwndDlg, IDOK), &rc1); MapWindowPoints(NULL, hwndDlg, (POINT *)&rc1, 2); GetWindowRect(GetDlgItem(hwndDlg, IDCANCEL), &rc2); MapWindowPoints(NULL, hwndDlg, (POINT *)&rc2, 2); if (pNewExt->bAdvanced) { rc1.top += pNewExt->dy; rc1.bottom += pNewExt->dy; rc2.top += pNewExt->dy; rc2.bottom += pNewExt->dy; ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_SHOWNOACTIVATE); ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), SW_SHOWNOACTIVATE); LoadStringW(shell32_hInstance, IDS_NEWEXT_ADVANCED_LEFT, szText, _countof(szText)); SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, szText); SetFocus(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX)); } else { rc1.top -= pNewExt->dy; rc1.bottom -= pNewExt->dy; rc2.top -= pNewExt->dy; rc2.bottom -= pNewExt->dy; ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_ASSOC), SW_HIDE); ShowWindow(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), SW_HIDE); LoadStringW(shell32_hInstance, IDS_NEWEXT_ADVANCED_RIGHT, szText, _countof(szText)); SetDlgItemTextW(hwndDlg, IDC_NEWEXT_ADVANCED, szText); rc.bottom -= pNewExt->dy; LoadStringW(shell32_hInstance, IDS_NEWEXT_NEW, szText, _countof(szText)); SetDlgItemTextW(hwndDlg, IDC_NEWEXT_COMBOBOX, szText); } HDWP hDWP = BeginDeferWindowPos(3); if (hDWP) hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDOK), NULL, rc1.left, rc1.top, rc1.right - rc1.left, rc1.bottom - rc1.top, SWP_NOACTIVATE | SWP_NOZORDER); if (hDWP) hDWP = DeferWindowPos(hDWP, GetDlgItem(hwndDlg, IDCANCEL), NULL, rc2.left, rc2.top, rc2.right - rc2.left, rc2.bottom - rc2.top, SWP_NOACTIVATE | SWP_NOZORDER); if (hDWP) hDWP = DeferWindowPos(hDWP, hwndDlg, NULL, rc.left, rc.top, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE | SWP_NOZORDER); if (hDWP) EndDeferWindowPos(hDWP); } static BOOL NewExtDlg_OnInitDialog(HWND hwndDlg, NEWEXT_DIALOG *pNewExt) { WCHAR szText[64]; pNewExt->hwndDlg = hwndDlg; pNewExt->bAdvanced = FALSE; GetWindowRect(hwndDlg, &pNewExt->rcDlg); RECT rc1, rc2; GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_EDIT), &rc1); GetWindowRect(GetDlgItem(hwndDlg, IDC_NEWEXT_COMBOBOX), &rc2); pNewExt->dy = rc2.top - rc1.top; LoadStringW(shell32_hInstance, IDS_NEWEXT_NEW, szText, _countof(szText)); SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_ADDSTRING, 0, (LPARAM)szText); SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_COMBOBOX, CB_SETCURSEL, 0, 0); SendDlgItemMessageW(hwndDlg, IDC_NEWEXT_EDIT, EM_SETLIMITTEXT, _countof(pNewExt->szExt) - 1, 0); NewExtDlg_OnAdvanced(hwndDlg, pNewExt); return TRUE; } static void StringTrimW(LPWSTR pszText) { LPWSTR pch = pszText; while (iswspace(*pch)) pch++; LPWSTR pchFirst, pchLast; pchFirst = pchLast = pch; while (*pch && !iswspace(*pch)) { ++pch; pchLast = pch; } INT_PTR cch = pchLast - pchFirst; MoveMemory(pszText, pchFirst, cch * sizeof(WCHAR)); pszText[cch] = 0; } static BOOL NewExtDlg_OnOK(HWND hwndDlg, NEWEXT_DIALOG *pNewExt) { LV_FINDINFO find; INT iItem; GetDlgItemTextW(hwndDlg, IDC_NEWEXT_EDIT, pNewExt->szExt, _countof(pNewExt->szExt)); StringTrimW(pNewExt->szExt); CharUpperW(pNewExt->szExt); GetDlgItemTextW(hwndDlg, IDC_NEWEXT_COMBOBOX, pNewExt->szFileType, _countof(pNewExt->szFileType)); StringTrimW(pNewExt->szFileType); if (pNewExt->szExt[0] == 0) { WCHAR szText[128], szTitle[128]; LoadStringW(shell32_hInstance, IDS_NEWEXT_SPECIFY_EXT, szText, _countof(szText)); szText[_countof(szText) - 1] = 0; LoadStringW(shell32_hInstance, IDS_FILE_TYPES, szTitle, _countof(szTitle)); szTitle[_countof(szTitle) - 1] = 0; MessageBoxW(hwndDlg, szText, szTitle, MB_ICONERROR); return FALSE; } ZeroMemory(&find, sizeof(find)); find.flags = LVFI_STRING; if (pNewExt->szExt[0] == L'.') { find.psz = &pNewExt->szExt[1]; } else { find.psz = pNewExt->szExt; } iItem = ListView_FindItem(pNewExt->hwndLV, -1, &find); if (iItem >= 0) { // already exists WCHAR szText[256], szFormat[256], szTitle[64], szFileType[64]; // get file type LV_ITEM item; ZeroMemory(&item, sizeof(item)); item.mask = LVIF_TEXT; item.pszText = szFileType; item.cchTextMax = _countof(szFileType); item.iItem = iItem; item.iSubItem = 1; ListView_GetItem(pNewExt->hwndLV, &item); // get text LoadStringW(shell32_hInstance, IDS_NEWEXT_ALREADY_ASSOC, szFormat, _countof(szFormat)); szText[_countof(szFormat) - 1] = 0; StringCchPrintfW(szText, _countof(szText), szFormat, find.psz, szFileType, find.psz, szFileType); // get title LoadStringW(shell32_hInstance, IDS_NEWEXT_EXT_IN_USE, szTitle, _countof(szTitle)); szTitle[_countof(szTitle) - 1] = 0; if (MessageBoxW(hwndDlg, szText, szTitle, MB_ICONWARNING | MB_YESNO) == IDNO) { return FALSE; } // Delete the item ListView_DeleteItem(pNewExt->hwndLV, iItem); } EndDialog(hwndDlg, IDOK); return TRUE; } // IDD_NEWEXTENSION dialog INT_PTR CALLBACK NewExtensionDlgProc( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { static NEWEXT_DIALOG *s_pNewExt = NULL; switch (uMsg) { case WM_INITDIALOG: s_pNewExt = (NEWEXT_DIALOG *)lParam; NewExtDlg_OnInitDialog(hwndDlg, s_pNewExt); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: NewExtDlg_OnOK(hwndDlg, s_pNewExt); break; case IDCANCEL: EndDialog(hwndDlg, IDCANCEL); break; case IDC_NEWEXT_ADVANCED: s_pNewExt->bAdvanced = !s_pNewExt->bAdvanced; NewExtDlg_OnAdvanced(hwndDlg, s_pNewExt); break; } break; } return 0; } static BOOL FileTypesDlg_AddExt(HWND hwndDlg, LPCWSTR pszExt, LPCWSTR pszFileType) { DWORD dwValue = 1; HKEY hKey; WCHAR szKey[13]; // max. "ft4294967295" + "\0" LONG nResult; // Search the next "ft%06u" key name do { StringCchPrintfW(szKey, _countof(szKey), TEXT("ft%06u"), dwValue); nResult = RegOpenKeyEx(HKEY_CLASSES_ROOT, szKey, 0, KEY_READ, &hKey); if (nResult != ERROR_SUCCESS) break; RegCloseKey(hKey); ++dwValue; } while (dwValue != 0); RegCloseKey(hKey); if (dwValue == 0) return FALSE; // Create new "ft%06u" key nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); if (ERROR_SUCCESS != nResult) return FALSE; RegCloseKey(hKey); // Create the ".ext" key WCHAR szExt[16]; if (*pszExt == L'.') ++pszExt; StringCchPrintfW(szExt, _countof(szExt), TEXT(".%s"), pszExt); CharLowerW(szExt); nResult = RegCreateKeyEx(HKEY_CLASSES_ROOT, szExt, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL); CharUpperW(szExt); if (ERROR_SUCCESS != nResult) return FALSE; // Set the default value of ".ext" to "ft%06u" DWORD dwSize = (lstrlen(szKey) + 1) * sizeof(WCHAR); RegSetValueExW(hKey, NULL, 0, REG_SZ, (BYTE *)szKey, dwSize); RegCloseKey(hKey); // Make up the file type name WCHAR szFile[100], szFileFormat[100]; LoadStringW(shell32_hInstance, IDS_FILE_EXT_TYPE, szFileFormat, _countof(szFileFormat)); szFile[_countof(szFileFormat) - 1] = 0; StringCchPrintfW(szFile, _countof(szFile), szFileFormat, &szExt[1]); // Insert an item to listview HWND hListView = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW); INT iItem = ListView_GetItemCount(hListView); INT iItemCopy = iItem; InsertFileType(hListView, szExt, &iItemCopy, szFile); LV_ITEM item; ZeroMemory(&item, sizeof(item)); item.mask = LVIF_STATE | LVIF_TEXT; item.iItem = iItem; item.state = LVIS_SELECTED | LVIS_FOCUSED; item.stateMask = LVIS_SELECTED | LVIS_FOCUSED; item.pszText = &szExt[1]; ListView_SetItem(hListView, &item); item.pszText = szFile; item.iSubItem = 1; ListView_SetItem(hListView, &item); ListView_EnsureVisible(hListView, iItem, FALSE); return TRUE; } // IDD_FOLDER_OPTIONS_FILETYPES dialog INT_PTR CALLBACK FolderOptionsFileTypesDlg( HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { LPNMLISTVIEW lppl; LVITEMW lvItem; WCHAR Buffer[255], FormatBuffer[255]; PFOLDER_FILE_TYPE_ENTRY pItem; OPENASINFO Info; NEWEXT_DIALOG newext; switch(uMsg) { case WM_INITDIALOG: pItem = InitializeFileTypesListCtrl(hwndDlg); /* Disable the Delete button if the listview is empty or the selected item should not be deleted by the user */ if (pItem == NULL || (pItem->EditFlags & 0x00000010)) // FTA_NoRemove EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), FALSE); return TRUE; case WM_COMMAND: switch(LOWORD(wParam)) { case IDC_FILETYPES_NEW: newext.hwndLV = GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW); if (IDOK == DialogBoxParamW(shell32_hInstance, MAKEINTRESOURCEW(IDD_NEWEXTENSION), hwndDlg, NewExtensionDlgProc, (LPARAM)&newext)) { FileTypesDlg_AddExt(hwndDlg, newext.szExt, newext.szFileType); } break; case IDC_FILETYPES_CHANGE: pItem = FindSelectedItem(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW)); if (pItem) { Info.oaifInFlags = OAIF_ALLOW_REGISTRATION | OAIF_REGISTER_EXT; Info.pcszClass = pItem->FileExtension; SHOpenWithDialog(hwndDlg, &Info); } break; } break; case WM_NOTIFY: lppl = (LPNMLISTVIEW) lParam; if (lppl->hdr.code == LVN_ITEMCHANGING) { ZeroMemory(&lvItem, sizeof(LVITEM)); lvItem.mask = LVIF_PARAM; lvItem.iItem = lppl->iItem; if (!SendMessageW(lppl->hdr.hwndFrom, LVM_GETITEMW, 0, (LPARAM)&lvItem)) return TRUE; pItem = (PFOLDER_FILE_TYPE_ENTRY)lvItem.lParam; if (!pItem) return TRUE; if (!(lppl->uOldState & LVIS_FOCUSED) && (lppl->uNewState & LVIS_FOCUSED)) { /* new focused item */ if (!LoadStringW(shell32_hInstance, IDS_FILE_DETAILS, FormatBuffer, sizeof(FormatBuffer) / sizeof(WCHAR))) { /* use default english format string */ wcscpy(FormatBuffer, L"Details for '%s' extension"); } /* format buffer */ swprintf(Buffer, FormatBuffer, &pItem->FileExtension[1]); /* update dialog */ SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DETAILS_GROUPBOX, Buffer); if (!LoadStringW(shell32_hInstance, IDS_FILE_DETAILSADV, FormatBuffer, sizeof(FormatBuffer) / sizeof(WCHAR))) { /* use default english format string */ wcscpy(FormatBuffer, L"Files with extension '%s' are of type '%s'. To change settings that affect all '%s' files, click Advanced."); } /* format buffer */ swprintf(Buffer, FormatBuffer, &pItem->FileExtension[1], &pItem->FileDescription[0], &pItem->FileDescription[0]); /* update dialog */ SetDlgItemTextW(hwndDlg, IDC_FILETYPES_DESCRIPTION, Buffer); /* Enable the Delete button */ if (pItem->EditFlags & 0x00000010) // FTA_NoRemove EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), FALSE); else EnableWindow(GetDlgItem(hwndDlg, IDC_FILETYPES_DELETE), TRUE); } } else if (lppl->hdr.code == PSN_SETACTIVE) { /* On page activation, set the focus to the listview */ SetFocus(GetDlgItem(hwndDlg, IDC_FILETYPES_LISTVIEW)); } break; } return FALSE; } static VOID ShowFolderOptionsDialog(HWND hWnd, HINSTANCE hInst) { PROPSHEETHEADERW pinfo; HPROPSHEETPAGE hppages[3]; HPROPSHEETPAGE hpage; UINT num_pages = 0; WCHAR szOptions[100]; hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_GENERAL, FolderOptionsGeneralDlg, 0, NULL); if (hpage) hppages[num_pages++] = hpage; hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_VIEW, FolderOptionsViewDlg, 0, NULL); if (hpage) hppages[num_pages++] = hpage; hpage = SH_CreatePropertySheetPage(IDD_FOLDER_OPTIONS_FILETYPES, FolderOptionsFileTypesDlg, 0, NULL); if (hpage) hppages[num_pages++] = hpage; szOptions[0] = L'\0'; LoadStringW(shell32_hInstance, IDS_FOLDER_OPTIONS, szOptions, sizeof(szOptions) / sizeof(WCHAR)); szOptions[(sizeof(szOptions)/sizeof(WCHAR))-1] = L'\0'; memset(&pinfo, 0x0, sizeof(PROPSHEETHEADERW)); pinfo.dwSize = sizeof(PROPSHEETHEADERW); pinfo.dwFlags = PSH_NOCONTEXTHELP; pinfo.nPages = num_pages; pinfo.phpage = hppages; pinfo.pszCaption = szOptions; PropertySheetW(&pinfo); } static VOID Options_RunDLLCommon(HWND hWnd, HINSTANCE hInst, int fOptions, DWORD nCmdShow) { switch(fOptions) { case 0: ShowFolderOptionsDialog(hWnd, hInst); break; case 1: // show taskbar options dialog FIXME("notify explorer to show taskbar options dialog"); //PostMessage(GetShellWindow(), WM_USER+22, fOptions, 0); break; default: FIXME("unrecognized options id %d\n", fOptions); } } /************************************************************************* * Options_RunDLL (SHELL32.@) */ EXTERN_C VOID WINAPI Options_RunDLL(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) { Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow); } /************************************************************************* * Options_RunDLLA (SHELL32.@) */ EXTERN_C VOID WINAPI Options_RunDLLA(HWND hWnd, HINSTANCE hInst, LPCSTR cmd, DWORD nCmdShow) { Options_RunDLLCommon(hWnd, hInst, StrToIntA(cmd), nCmdShow); } /************************************************************************* * Options_RunDLLW (SHELL32.@) */ EXTERN_C VOID WINAPI Options_RunDLLW(HWND hWnd, HINSTANCE hInst, LPCWSTR cmd, DWORD nCmdShow) { Options_RunDLLCommon(hWnd, hInst, StrToIntW(cmd), nCmdShow); }