From 4f7736a088ff3800f7e755c726879e52ae4e5dee Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Tue, 3 Jun 2025 20:39:38 +0200 Subject: [PATCH] [SHELL32] Implement single-click mode in DefView (#8054) --- dll/win32/shell32/CDefView.cpp | 38 +++++--- dll/win32/shell32/dialogs/general.cpp | 119 +++++++++++++++----------- dll/win32/shell32/shfldr.h | 2 + 3 files changed, 99 insertions(+), 60 deletions(-) diff --git a/dll/win32/shell32/CDefView.cpp b/dll/win32/shell32/CDefView.cpp index c67cc83c028..f865e7d28d3 100644 --- a/dll/win32/shell32/CDefView.cpp +++ b/dll/win32/shell32/CDefView.cpp @@ -346,7 +346,7 @@ public: LRESULT DoColumnContextMenu(LRESULT lParam); UINT GetSelections(); SFGAOF GetSelectionAttributes(SFGAOF Query); - HRESULT OpenSelectedItems(); + HRESULT OpenSelectedItems(PCSTR pszVerb = NULL); void OnDeactivate(); void DoActivate(UINT uState); HRESULT drag_notify_subitem(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect); @@ -357,6 +357,7 @@ public: HRESULT LoadViewState(); HRESULT SaveViewState(IStream *pStream); void UpdateFolderViewFlags(); + UINT GetItemActivateFlags(); DWORD GetCommDlgViewFlags() { @@ -906,9 +907,9 @@ BOOL CDefView::CreateList() if (m_FolderSettings.fFlags & FWF_FULLROWSELECT) ListExStyle |= LVS_EX_FULLROWSELECT; - if ((m_FolderSettings.fFlags & FWF_SINGLECLICKACTIVATE) || - (!SHELL_GetSetting(SSF_DOUBLECLICKINWEBVIEW, fDoubleClickInWebView) && !SHELL_GetSetting(SSF_WIN95CLASSIC, fWin95Classic))) - ListExStyle |= LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE; + ListExStyle |= GetItemActivateFlags(); + if (ListExStyle & LVS_EX_ONECLICKACTIVATE) + ListExStyle |= SHELL_GetIconUnderlineFlags(); if (m_FolderSettings.fFlags & FWF_NOCOLUMNHEADER) dwStyle |= LVS_NOCOLUMNHEADER; @@ -2184,7 +2185,7 @@ HRESULT CDefView::InvokeContextMenuCommand(CComPtr& pCM, LPCSTR lp return S_OK; } -HRESULT CDefView::OpenSelectedItems() +HRESULT CDefView::OpenSelectedItems(PCSTR pszVerb) { HMENU hMenu; UINT uCommand; @@ -2213,13 +2214,15 @@ HRESULT CDefView::OpenSelectedItems() return hResult; uCommand = GetMenuDefaultItem(hMenu, FALSE, 0); - if (uCommand == (UINT)-1) + if (uCommand == (UINT)-1 && !pszVerb) { ERR("GetMenuDefaultItem returned -1\n"); return E_FAIL; } + if (!pszVerb) + pszVerb = MAKEINTRESOURCEA(uCommand); - InvokeContextMenuCommand(pCM, MAKEINTRESOURCEA(uCommand), NULL); + InvokeContextMenuCommand(pCM, pszVerb, NULL); return hResult; } @@ -2681,11 +2684,9 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl break; case NM_DBLCLK: TRACE("-- NM_DBLCLK %p\n", this); - OpenSelectedItems(); break; case NM_RETURN: TRACE("-- NM_RETURN %p\n", this); - OpenSelectedItems(); break; case HDN_ENDTRACKW: TRACE("-- HDN_ENDTRACKW %p\n", this); @@ -2705,7 +2706,7 @@ LRESULT CDefView::OnNotify(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL &bHandl break; case LVN_ITEMACTIVATE: TRACE("-- LVN_ITEMACTIVATE %p\n", this); - OnStateChange(CDBOSC_SELCHANGE); // browser will get the IDataObject + OpenSelectedItems(((NMITEMACTIVATE *)lpnmh)->uKeyFlags & LVKF_ALT ? "properties" : NULL); break; case LVN_COLUMNCLICK: { @@ -3028,6 +3029,15 @@ LRESULT CDefView::OnSettingChange(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL if (wParam == SPI_SETDESKWALLPAPER || wParam == 0) UpdateListColors(); + UINT ListExMask = LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE; + UINT ListExBits = GetItemActivateFlags(); + if (wParam == SPI_GETICONTITLELOGFONT || + (lParam && !lstrcmpiW((PWSTR)lParam, REGSTR_PATH_EXPLORER L"\\IconUnderline"))) + { + ListExMask |= LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD; + ListExBits |= SHELL_GetIconUnderlineFlags(); + } + m_ListView.SetExtendedListViewStyle(ListExBits, ListExMask); m_ListView.SendMessage(uMsg, wParam, lParam); return S_OK; } @@ -3458,6 +3468,14 @@ void CDefView::UpdateFolderViewFlags() UPDATEFOLDERVIEWFLAGS(m_FolderSettings.fFlags, FWF_NOGROUPING, !ListView_IsGroupViewEnabled(m_ListView.m_hWnd)); } +UINT CDefView::GetItemActivateFlags() +{ + SHELLSTATE ss; + SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW | SSF_WIN95CLASSIC, FALSE); + return ((m_FolderSettings.fFlags & FWF_SINGLECLICKACTIVATE) || (!ss.fDoubleClickInWebView && !ss.fWin95Classic)) + ? (LVS_EX_TRACKSELECT | LVS_EX_ONECLICKACTIVATE) : 0; +} + HRESULT WINAPI CDefView::SelectItem(PCUITEMID_CHILD pidl, UINT uFlags) { int i; diff --git a/dll/win32/shell32/dialogs/general.cpp b/dll/win32/shell32/dialogs/general.cpp index 60d304f7404..9f2612ca61a 100644 --- a/dll/win32/shell32/dialogs/general.cpp +++ b/dll/win32/shell32/dialogs/general.cpp @@ -23,8 +23,9 @@ WINE_DEFAULT_DEBUG_CHANNEL (fprop); -static const LPCWSTR s_pszExplorerKey = - L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"; +static const LPCWSTR s_pszExplorerKey = REGSTR_PATH_EXPLORER; + +enum { UNDERLINE_ON, UNDERLINE_OFF, UNDERLINE_HOVER, UNDERLINE_IE }; ///////////////////////////////////////////////////////////////////////////// // Shell settings @@ -60,10 +61,10 @@ SHELL32_WriteRegShellState(PREGSHELLSTATE prss) // bDoubleClick is FALSE if "Single-click to open an item (point to select)". ////////////////////////////////////////////////////////////////////////////// // API Monitor: -// SHLWAPI.dll RegOpenKeyExW ( 0x000000c8, NULL, 0, MAXIMUM_ALLOWED, 0x00def234 ) ERROR_SUCCESS 0.0000243 -// SHLWAPI.dll RegSetValueExW ( 0x000003e8, "ShellState", 0, REG_BINARY, 0x000c2050, 36 ) ERROR_SUCCESS 0.0001028 -// SHLWAPI.dll RegCloseKey ( 0x000003e8 ) ERROR_SUCCESS 0.0000081 -// Explorer.EXE SHSettingsChanged ( 0, "ShellState" ) 0.0000131 +// SHLWAPI.dll RegOpenKeyExW ( 0x000000c8, NULL, 0, MAXIMUM_ALLOWED, 0x00def234 ) ERROR_SUCCESS +// SHLWAPI.dll RegSetValueExW ( 0x000003e8, "ShellState", 0, REG_BINARY, 0x000c2050, 36 ) ERROR_SUCCESS +// SHLWAPI.dll RegCloseKey ( 0x000003e8 ) ERROR_SUCCESS +// Explorer.EXE SHSettingsChanged ( 0, "ShellState" ) (WM_SETTINGCHANGE handling) static BOOL IntSetShellStateSettings(BOOL bDoubleClick, BOOL bUseCommonTasks) { @@ -71,17 +72,9 @@ IntSetShellStateSettings(BOOL bDoubleClick, BOOL bUseCommonTasks) shellstate.fDoubleClickInWebView = !!bDoubleClick; shellstate.fWebView = !!bUseCommonTasks; SHGetSetSettings(&shellstate, SSF_DOUBLECLICKINWEBVIEW | SSF_WEBVIEW, TRUE); - - // FIXME: This is not correct, it does nothing. SHGetSetSettings will broadcast it. - SHSettingsChanged(0, L"ShellState"); return TRUE; } -////////////////////////////////////////////////////////////////////////////// -// API Monitor: -// SHLWAPI.dll RegOpenKeyExW ( 0x000000c8, NULL, 0, MAXIMUM_ALLOWED, 0x0007e484 ) ERROR_SUCCESS 0.0000388 -// SHLWAPI.dll RegQueryValueExW ( 0x000005a8, "ShellState", NULL, 0x0007e474, 0x000c2050, 0x0007e4fc ) ERROR_SUCCESS 0.0000271 -// SHLWAPI.dll RegCloseKey ( 0x000005a8 ) ERROR_SUCCESS 0.0000112 EXTERN_C BOOL SHELL32_ReadRegShellState(PREGSHELLSTATE prss) { @@ -91,32 +84,61 @@ SHELL32_ReadRegShellState(PREGSHELLSTATE prss) return err == ERROR_SUCCESS && prss->dwSize >= REGSHELLSTATE_SIZE; } -// bIconUnderline is TRUE if "Underline icon titles only when I point at them". -// bIconUnderline is FALSE if "Underline icon titles consistent with my browser". +// bUnderlineHover is TRUE if "Underline icon titles only when I point at them". +// bUnderlineHover is FALSE if "Underline icon titles consistent with my browser". ////////////////////////////////////////////////////////////////////////////// // API Monitor: -// SHELL32.dll SHRegGetUSValueW ( "Software\Microsoft\Windows\CurrentVersion\Explorer", "IconUnderline", NULL, 0x00d2f324, 0x00d2f328, FALSE, 0x00d2f32c, 4 ) ERROR_SUCCESS 0.0002484 -// SHELL32.dll IsDlgButtonChecked ( 0x0005009e, 30104 ) BST_CHECKED 0.0000212 -// SHELL32.dll SHRegSetUSValueW ( "Software\Microsoft\Windows\CurrentVersion\Explorer", "IconUnderline", 0, 0x00d2f314, 4, 6 ) ERROR_SUCCESS 0.0008300 -// Explorer.EXE SHSettingsChanged ( 0, "Software\Microsoft\Windows\CurrentVersion\Explorer\IconUnderline" ) 0.0000092 -static BOOL IntSetUnderlineState(BOOL bIconUnderline) +// SHELL32.dll SHRegGetUSValueW ( "Software\Microsoft\Windows\CurrentVersion\Explorer", "IconUnderline", NULL, 0x00d2f324, 0x00d2f328, FALSE, 0x00d2f32c, 4 ) ERROR_SUCCESS +// SHELL32.dll IsDlgButtonChecked ( 0x0005009e, 30104 ) BST_CHECKED +// SHELL32.dll SHRegSetUSValueW ( "Software\Microsoft\Windows\CurrentVersion\Explorer", "IconUnderline", 0, 0x00d2f314, 4, 6 ) ERROR_SUCCESS +// Explorer.EXE SHSettingsChanged ( 0, "Software\Microsoft\Windows\CurrentVersion\Explorer\IconUnderline" ) (WM_SETTINGCHANGE handling) +static BOOL IntSetUnderlineState(BOOL bUnderlineHover) { LSTATUS Status; - DWORD dwValue = (bIconUnderline ? 3 : 2), dwSize = sizeof(DWORD); + DWORD dwValue = bUnderlineHover ? UNDERLINE_HOVER : UNDERLINE_IE; Status = SHRegSetUSValue(s_pszExplorerKey, L"IconUnderline", REG_NONE, - &dwValue, dwSize, SHREGSET_FORCE_HKCU | SHREGSET_HKLM); + &dwValue, sizeof(dwValue), SHREGSET_FORCE_HKCU | SHREGSET_HKLM); if (Status != ERROR_SUCCESS) return FALSE; - SHSendMessageBroadcastW(WM_SETTINGCHANGE, 0, (LPARAM)L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\IconUnderline"); + SHSendMessageBroadcastW(WM_SETTINGCHANGE, 0, (LPARAM)(REGSTR_PATH_EXPLORER L"\\IconUnderline")); return TRUE; } -static BOOL IntGetUnderlineState(VOID) +static UINT IntGetRawIconUnderlineValue() { - DWORD dwValue, dwDefault = 2, dwSize = sizeof(DWORD); - SHRegGetUSValue(s_pszExplorerKey, L"IconUnderline", NULL, &dwValue, &dwSize, FALSE, &dwDefault, sizeof(DWORD)); - return dwValue == 3; + DWORD dwValue, dwDefault = UNDERLINE_ON; + DWORD dwSize = sizeof(dwValue); + SHRegGetUSValue(s_pszExplorerKey, L"IconUnderline", NULL, &dwValue, &dwSize, FALSE, &dwDefault, sizeof(dwDefault)); + return dwValue; +} + +static UINT SHELL_GetIconUnderlineMode() +{ + UINT mode = IntGetRawIconUnderlineValue(); + if (mode < UNDERLINE_IE) + return mode; + + WCHAR buf[sizeof("hoverX")]; + *buf = UNICODE_NULL; + DWORD cb = sizeof(buf); + SHRegGetUSValueW(L"Software\\Microsoft\\Internet Explorer\\Main", + L"Anchor Underline", NULL, buf, &cb, FALSE, buf, cb); + if (!lstrcmpiW(buf, L"no")) + return UNDERLINE_OFF; + if (!lstrcmpiW(buf, L"hover")) + return UNDERLINE_HOVER; + return UNDERLINE_ON; +} + +UINT SHELL_GetIconUnderlineFlags() +{ + UINT mode = SHELL_GetIconUnderlineMode(); + if (mode == UNDERLINE_HOVER) + return LVS_EX_UNDERLINEHOT; + if (mode == UNDERLINE_ON) + return LVS_EX_UNDERLINEHOT | LVS_EX_UNDERLINECOLD; + return 0; } // bNewWindowMode is TRUE if "Open each folder in its own window". @@ -239,7 +261,6 @@ GeneralDlg_UpdateIcons(HWND hDlg, UINT nCtrlID, PGENERAL_DIALOG pGeneral) if (nCtrlID == IDC_FOLDER_OPTIONS_DOUBLECLICK) { - CheckRadioButton(hDlg, IDC_FOLDER_OPTIONS_ULBROWSER, IDC_FOLDER_OPTIONS_ULPOINT, IDC_FOLDER_OPTIONS_ULBROWSER); EnableWindow(GetDlgItem(hDlg, IDC_FOLDER_OPTIONS_ULBROWSER), FALSE); EnableWindow(GetDlgItem(hDlg, IDC_FOLDER_OPTIONS_ULPOINT), FALSE); } @@ -247,9 +268,16 @@ GeneralDlg_UpdateIcons(HWND hDlg, UINT nCtrlID, PGENERAL_DIALOG pGeneral) static void GeneralDlg_StoreToUI(HWND hwndDlg, BOOL bDoubleClick, BOOL bUseCommonTasks, - BOOL bUnderline, BOOL bNewWindowMode, PGENERAL_DIALOG pGeneral) + BOOL bUnderlineHover, BOOL bNewWindowMode, PGENERAL_DIALOG pGeneral) { - EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_COMMONTASKS), bUseCommonTasks); // FIXME: ROS DefView does not support WebView nor the tasks pane + if (SHRestricted(REST_CLASSICSHELL)) + { + EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_SINGLECLICK), FALSE); + bDoubleClick = TRUE; + bUseCommonTasks = FALSE; + } + + EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_COMMONTASKS), bUseCommonTasks); // TODO: ROS DefView does not support WebView nor the tasks pane if (bUseCommonTasks) CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_COMMONTASKS, IDC_FOLDER_OPTIONS_CLASSICFOLDERS, IDC_FOLDER_OPTIONS_COMMONTASKS); @@ -266,21 +294,12 @@ GeneralDlg_StoreToUI(HWND hwndDlg, BOOL bDoubleClick, BOOL bUseCommonTasks, else CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_SAMEWINDOW, IDC_FOLDER_OPTIONS_OWNWINDOW, IDC_FOLDER_OPTIONS_SAMEWINDOW); - if (!bDoubleClick) - { - EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER), TRUE); - EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULPOINT), TRUE); - if (bUnderline) - CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER, IDC_FOLDER_OPTIONS_ULPOINT, IDC_FOLDER_OPTIONS_ULPOINT); - else - CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER, IDC_FOLDER_OPTIONS_ULPOINT, IDC_FOLDER_OPTIONS_ULBROWSER); - } + EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER), !bDoubleClick); + EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULPOINT), !bDoubleClick); + if (bUnderlineHover) + CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER, IDC_FOLDER_OPTIONS_ULPOINT, IDC_FOLDER_OPTIONS_ULPOINT); else - { - EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER), FALSE); - EnableWindow(GetDlgItem(hwndDlg, IDC_FOLDER_OPTIONS_ULPOINT), FALSE); CheckRadioButton(hwndDlg, IDC_FOLDER_OPTIONS_ULBROWSER, IDC_FOLDER_OPTIONS_ULPOINT, IDC_FOLDER_OPTIONS_ULBROWSER); - } } static BOOL @@ -290,10 +309,10 @@ GeneralDlg_OnInitDialog(HWND hwndDlg, PGENERAL_DIALOG pGeneral) SHGetSetSettings(&ss, SSF_DOUBLECLICKINWEBVIEW | SSF_WEBVIEW, FALSE); BOOL bDoubleClick = !!ss.fDoubleClickInWebView; BOOL bUseCommonTasks = !!ss.fWebView; - BOOL bUnderline = IntGetUnderlineState(); + BOOL bUnderlineHover = IntGetRawIconUnderlineValue() == UNDERLINE_HOVER; BOOL bNewWindowMode = IntGetNewWindowMode(); - GeneralDlg_StoreToUI(hwndDlg, bDoubleClick, bUseCommonTasks, bUnderline, bNewWindowMode, pGeneral); + GeneralDlg_StoreToUI(hwndDlg, bDoubleClick, bUseCommonTasks, bUnderlineHover, bNewWindowMode, pGeneral); GeneralDlg_UpdateIcons(hwndDlg, 0, pGeneral); return TRUE; @@ -305,10 +324,10 @@ GeneralDlg_OnRestoreDefaults(HWND hwndDlg, PGENERAL_DIALOG pGeneral) // default values BOOL bDoubleClick = TRUE; BOOL bUseCommonTasks = TRUE; - BOOL bUnderline = FALSE; + BOOL bUnderlineHover = FALSE; BOOL bNewWindowMode = (_WIN32_WINNT < _WIN32_WINNT_WIN2K); - GeneralDlg_StoreToUI(hwndDlg, bDoubleClick, bUseCommonTasks, bUnderline, bNewWindowMode, pGeneral); + GeneralDlg_StoreToUI(hwndDlg, bDoubleClick, bUseCommonTasks, bUnderlineHover, bNewWindowMode, pGeneral); GeneralDlg_UpdateIcons(hwndDlg, 0, pGeneral); } @@ -317,10 +336,10 @@ GeneralDlg_OnApply(HWND hwndDlg, PGENERAL_DIALOG pGeneral) { BOOL bDoubleClick = !(IsDlgButtonChecked(hwndDlg, IDC_FOLDER_OPTIONS_SINGLECLICK) == BST_CHECKED); BOOL bUseCommonTasks = (IsDlgButtonChecked(hwndDlg, IDC_FOLDER_OPTIONS_COMMONTASKS) == BST_CHECKED); - BOOL bUnderline = (IsDlgButtonChecked(hwndDlg, IDC_FOLDER_OPTIONS_ULPOINT) == BST_CHECKED); + BOOL bUnderlineHover = (IsDlgButtonChecked(hwndDlg, IDC_FOLDER_OPTIONS_ULPOINT) == BST_CHECKED); BOOL bNewWindowMode = !(IsDlgButtonChecked(hwndDlg, IDC_FOLDER_OPTIONS_SAMEWINDOW) == BST_CHECKED); - IntSetUnderlineState(bUnderline); + IntSetUnderlineState(bUnderlineHover); BOOL updateCabinets = IntSetNewWindowMode(bNewWindowMode) == S_OK; IntSetShellStateSettings(bDoubleClick, bUseCommonTasks); if (updateCabinets) diff --git a/dll/win32/shell32/shfldr.h b/dll/win32/shell32/shfldr.h index fb04ea565ea..a21be88de53 100644 --- a/dll/win32/shell32/shfldr.h +++ b/dll/win32/shell32/shfldr.h @@ -207,4 +207,6 @@ HRESULT inline SHSetStrRet(LPSTRRET pStrRet, DWORD resId) #endif +UINT SHELL_GetIconUnderlineFlags(); + #endif /* _SHFLDR_H_ */