From a444572c3e0631ab176ac0181f9f74baf8a6b29c Mon Sep 17 00:00:00 2001 From: Whindmar Saksit Date: Thu, 27 Mar 2025 13:43:58 +0100 Subject: [PATCH] [SHIMGVW] Display error message on delete failure (#7776) CORE-20019 --- dll/win32/shimgvw/resource.h | 3 ++ dll/win32/shimgvw/shimgvw.c | 98 +++++++++++++++++++++--------------- dll/win32/shimgvw/shimgvw.h | 1 + dll/win32/shimgvw/shimgvw.rc | 1 + dll/win32/shimgvw/util.c | 14 ++++-- 5 files changed, 73 insertions(+), 44 deletions(-) diff --git a/dll/win32/shimgvw/resource.h b/dll/win32/shimgvw/resource.h index 17fce85b7fc..8398a23818e 100644 --- a/dll/win32/shimgvw/resource.h +++ b/dll/win32/shimgvw/resource.h @@ -10,6 +10,9 @@ /* Cursors */ #define IDC_HANDDRAG 100 +/* Control IDs */ +#define IDC_TOOLBAR 40 + /* Toolbar images */ #define IDB_PREV_PIC 50 #define IDB_NEXT_PIC 51 diff --git a/dll/win32/shimgvw/shimgvw.c b/dll/win32/shimgvw/shimgvw.c index 392a0a463be..3084da58bc9 100644 --- a/dll/win32/shimgvw/shimgvw.c +++ b/dll/win32/shimgvw/shimgvw.c @@ -31,6 +31,7 @@ HINSTANCE g_hInstance = NULL; HWND g_hMainWnd = NULL; HWND g_hwndFullscreen = NULL; SHIMGVW_FILENODE * g_pCurrentFile = NULL; +WCHAR g_szFile[MAX_PATH]; GpImage * g_pImage = NULL; SHIMGVW_SETTINGS g_Settings; UINT g_ImageId; @@ -119,7 +120,6 @@ typedef struct tagPREVIEW_DATA UINT m_nTimerInterval; BOOL m_bHideCursor; POINT m_ptOrigin; - WCHAR m_szFile[MAX_PATH]; } PREVIEW_DATA, *PPREVIEW_DATA; static VOID Preview_ToggleSlideShowEx(PPREVIEW_DATA pData, BOOL StartTimer); @@ -374,18 +374,20 @@ Preview_pFreeImage(PPREVIEW_DATA pData) GdipDisposeImage(g_pImage); g_pImage = NULL; } - - pData->m_szFile[0] = UNICODE_NULL; } static VOID Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) { HRESULT hr; + BOOL bCanDel; Preview_pFreeImage(pData); InvalidateRect(pData->m_hwnd, NULL, FALSE); /* Schedule redraw in case we change to "No preview" */ + GetFullPathNameW(szOpenFileName, _countof(g_szFile), g_szFile, NULL); hr = LoadImageFromPath(szOpenFileName, &g_pImage); + bCanDel = hr != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) && hr != HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND); + SendDlgItemMessageW(g_hMainWnd, IDC_TOOLBAR, TB_ENABLEBUTTON, IDC_DELETE, bCanDel); if (FAILED(hr)) { DPRINT1("GdipLoadImageFromStream() failed, %d\n", hr); @@ -396,17 +398,14 @@ Preview_pLoadImage(PPREVIEW_DATA pData, LPCWSTR szOpenFileName) Anime_LoadInfo(&pData->m_Anime); - GetFullPathNameW(szOpenFileName, _countof(pData->m_szFile), pData->m_szFile, NULL); - SHAddToRecentDocs(SHARD_PATHW, pData->m_szFile); - /* Reset zoom and redraw display */ Preview_ResetZoom(pData); - - Preview_UpdateTitle(pData, szOpenFileName); + SHAddToRecentDocs(SHARD_PATHW, g_szFile); + Preview_UpdateTitle(pData, g_szFile); ++g_ImageId; - EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_PRINT, L"print", pData->m_szFile); - EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_MODIFY, L"edit", pData->m_szFile); + EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_PRINT, L"print", g_szFile); + EnableCommandIfVerbExists(g_ImageId, g_hMainWnd, IDC_MODIFY, L"edit", g_szFile); } static VOID @@ -562,7 +561,7 @@ Preview_pSaveImageAs(PPREVIEW_DATA pData) static VOID Preview_pPrintImage(PPREVIEW_DATA pData) { - ShellExecuteVerb(g_hMainWnd, L"print", pData->m_szFile, FALSE); + ShellExecuteVerb(g_hMainWnd, L"print", g_szFile, FALSE); } static VOID @@ -584,6 +583,14 @@ Preview_UpdateImage(PPREVIEW_DATA pData) ZoomWnd_UpdateScroll(pData, TRUE); } +static VOID +Preview_LoadImage(PPREVIEW_DATA pData, SHIMGVW_FILENODE *pNode) +{ + Preview_pLoadImageFromNode(pData, pNode); + Preview_UpdateImage(pData); + Preview_UpdateUI(pData); +} + static SHIMGVW_FILENODE* pBuildFileList(LPCWSTR szFirstFile) { @@ -948,7 +955,7 @@ Preview_CreateToolBar(PPREVIEW_DATA pData) style |= CCS_BOTTOM; hwndToolBar = CreateWindowExW(0, TOOLBARCLASSNAMEW, NULL, style, - 0, 0, 0, 0, pData->m_hwnd, NULL, g_hInstance, NULL); + 0, 0, 0, 0, pData->m_hwnd, (HMENU)IDC_TOOLBAR, g_hInstance, NULL); if (!hwndToolBar) return FALSE; @@ -1217,8 +1224,8 @@ ZoomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) break; } case WM_CONTEXTMENU: - if (Preview_IsMainWnd(pData->m_hwnd)) - DoShellContextMenuOnFile(hwnd, pData->m_szFile, lParam); + if (Preview_IsMainWnd(pData->m_hwnd) && *g_szFile) + DoShellContextMenuOnFile(hwnd, g_szFile, lParam); break; case WM_HSCROLL: case WM_VSCROLL: @@ -1302,9 +1309,7 @@ Preview_OnCreate(HWND hwnd, LPCREATESTRUCT pCS) PathUnquoteSpacesW(szFile); g_pCurrentFile = pBuildFileList(szFile); - Preview_pLoadImageFromNode(pData, g_pCurrentFile); - Preview_UpdateImage(pData); - Preview_UpdateUI(pData); + Preview_LoadImage(pData, g_pCurrentFile); } return TRUE; @@ -1368,14 +1373,11 @@ static VOID Preview_Delete(PPREVIEW_DATA pData) { WCHAR szCurFile[MAX_PATH + 1], szNextFile[MAX_PATH]; - HWND hwnd = pData->m_hwnd; - SHFILEOPSTRUCTW FileOp = { hwnd, FO_DELETE }; - - if (!pData->m_szFile[0]) - return; + SHFILEOPSTRUCTW FileOp = { pData->m_hwnd, FO_DELETE }; + UINT error; /* FileOp.pFrom must be double-null-terminated */ - GetFullPathNameW(pData->m_szFile, _countof(szCurFile) - 1, szCurFile, NULL); + GetFullPathNameW(g_szFile, _countof(szCurFile) - 1, szCurFile, NULL); szCurFile[_countof(szCurFile) - 2] = UNICODE_NULL; /* Avoid buffer overrun */ szCurFile[lstrlenW(szCurFile) + 1] = UNICODE_NULL; @@ -1388,24 +1390,27 @@ Preview_Delete(PPREVIEW_DATA pData) /* Confirm file deletion and delete if allowed */ FileOp.pFrom = szCurFile; - FileOp.fFlags = FOF_ALLOWUNDO; - if (SHFileOperationW(&FileOp) != 0) + FileOp.fFlags = GetKeyState(VK_SHIFT) < 0 ? 0 : FOF_ALLOWUNDO; + error = g_szFile[0] ? SHFileOperationW(&FileOp) : ERROR_FILE_NOT_FOUND; + if (error != 0) { - DPRINT("Preview_Delete: SHFileOperationW() failed or canceled\n"); + enum { SHFO_FIRSTCUSTOMERROR = 0x71 /* DE_SAMEFILE */ }; + if (!FileOp.fAnyOperationsAborted && error != ERROR_CANCELLED) + ErrorBox(FileOp.hwnd, error < SHFO_FIRSTCUSTOMERROR ? error : E_FAIL); return; } /* Reload the file list and go next file */ pFreeFileList(g_pCurrentFile); g_pCurrentFile = pBuildFileList(szNextFile); - Preview_pLoadImageFromNode(pData, g_pCurrentFile); + Preview_LoadImage(pData, g_pCurrentFile); } static VOID Preview_Edit(HWND hwnd) { PPREVIEW_DATA pData = Preview_GetData(g_hMainWnd); - ShellExecuteVerb(pData->m_hwnd, L"edit", pData->m_szFile, TRUE); + ShellExecuteVerb(pData->m_hwnd, L"edit", g_szFile, TRUE); } static VOID @@ -1452,16 +1457,28 @@ Preview_GoNextPic(PPREVIEW_DATA pData, BOOL bNext) g_pCurrentFile = g_pCurrentFile->Next; else g_pCurrentFile = g_pCurrentFile->Prev; - Preview_pLoadImageFromNode(pData, g_pCurrentFile); - Preview_UpdateImage(pData); - Preview_UpdateUI(pData); + Preview_LoadImage(pData, g_pCurrentFile); } } +static HRESULT +IsCommandEnabled(UINT nCmdId) +{ + HWND hToolbar = GetDlgItem(g_hMainWnd, IDC_TOOLBAR); + LRESULT Ok = SendMessageW(hToolbar, TB_ISBUTTONENABLED, nCmdId, 0); + return Ok ? S_OK : SendMessageW(hToolbar, TB_COMMANDTOINDEX, nCmdId, 0) != -1 ? S_FALSE : E_FAIL; +} + static VOID -Preview_OnCommand(HWND hwnd, UINT nCommandID) +Preview_OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam) { PPREVIEW_DATA pData = Preview_GetData(hwnd); + UINT nCommandID = LOWORD(wParam); + if ((!HIWORD(wParam) || !lParam) && IsCommandEnabled(nCommandID) == S_FALSE) + { + MessageBeep(MB_ICONWARNING); + return; + } switch (nCommandID) { @@ -1544,7 +1561,7 @@ Preview_OnCommand(HWND hwnd, UINT nCommandID) if (g_pImage) { GdipImageRotateFlip(g_pImage, Rotate270FlipNone); - Preview_pSaveImage(pData, pData->m_szFile); + Preview_pSaveImage(pData, g_szFile); Preview_UpdateImage(pData); } break; @@ -1553,15 +1570,13 @@ Preview_OnCommand(HWND hwnd, UINT nCommandID) if (g_pImage) { GdipImageRotateFlip(g_pImage, Rotate90FlipNone); - Preview_pSaveImage(pData, pData->m_szFile); + Preview_pSaveImage(pData, g_szFile); Preview_UpdateImage(pData); } break; case IDC_DELETE: Preview_Delete(pData); - Preview_UpdateImage(pData); - Preview_UpdateUI(pData); break; case IDC_MODIFY: @@ -1629,7 +1644,7 @@ Preview_OnDropFiles(HWND hwnd, HDROP hDrop) pFreeFileList(g_pCurrentFile); g_pCurrentFile = pBuildFileList(szFile); - Preview_pLoadImageFromNode(pData, g_pCurrentFile); + Preview_LoadImage(pData, g_pCurrentFile); DragFinish(hDrop); } @@ -1647,7 +1662,7 @@ PreviewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } case WM_COMMAND: { - Preview_OnCommand(hwnd, LOWORD(wParam)); + Preview_OnCommand(hwnd, wParam, lParam); break; } case WM_NOTIFY: @@ -1732,14 +1747,15 @@ ImageView_Main(HWND hwnd, LPCWSTR szFileName) HACCEL hAccel; HRESULT hrCoInit; INITCOMMONCONTROLSEX Icc = { .dwSize = sizeof(Icc), .dwICC = ICC_WIN95_CLASSES }; + g_szFile[0] = UNICODE_NULL; InitCommonControlsEx(&Icc); SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); // Give UI higher priority than background threads /* Initialize COM */ - hrCoInit = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); + hrCoInit = OleInitialize(NULL); if (FAILED(hrCoInit)) - DPRINT1("Warning, CoInitializeEx failed with code=%08X\n", (int)hrCoInit); + DPRINT1("Warning, OleInitialize failed with code=%08X\n", (int)hrCoInit); if (!ImageView_LoadSettings()) ImageView_ResetSettings(); @@ -1807,7 +1823,7 @@ ImageView_Main(HWND hwnd, LPCWSTR szFileName) /* Release COM resources */ if (SUCCEEDED(hrCoInit)) - CoUninitialize(); + OleUninitialize(); return 0; } diff --git a/dll/win32/shimgvw/shimgvw.h b/dll/win32/shimgvw/shimgvw.h index cdb6f975fa2..4f3c741a3ed 100644 --- a/dll/win32/shimgvw/shimgvw.h +++ b/dll/win32/shimgvw/shimgvw.h @@ -76,6 +76,7 @@ BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam); void DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam); void EnableCommandIfVerbExists(UINT ImageId, HWND hwnd, UINT CmdId, PCWSTR Verb, PCWSTR File); void ShellExecuteVerb(HWND hwnd, PCWSTR Verb, PCWSTR File, BOOL Quit); +UINT ErrorBox(HWND hwnd, UINT Error); void DisplayHelp(HWND hwnd); static inline LPVOID QuickAlloc(SIZE_T cbSize, BOOL bZero) diff --git a/dll/win32/shimgvw/shimgvw.rc b/dll/win32/shimgvw/shimgvw.rc index bec550cb4c3..3efd98fbdb4 100644 --- a/dll/win32/shimgvw/shimgvw.rc +++ b/dll/win32/shimgvw/shimgvw.rc @@ -59,6 +59,7 @@ BEGIN "K", IDC_ROT_CLOCKW, VIRTKEY, CONTROL "L", IDC_ROT_COUNCW, VIRTKEY, CONTROL VK_DELETE, IDC_DELETE, VIRTKEY + VK_DELETE, IDC_DELETE, VIRTKEY, SHIFT "P", IDC_PRINT, VIRTKEY, CONTROL "S", IDC_SAVEAS, VIRTKEY, CONTROL "E", IDC_MODIFY, VIRTKEY, CONTROL diff --git a/dll/win32/shimgvw/util.c b/dll/win32/shimgvw/util.c index 5a3b8d46294..491e6660ba3 100644 --- a/dll/win32/shimgvw/util.c +++ b/dll/win32/shimgvw/util.c @@ -62,7 +62,8 @@ ModifyShellContextMenu(IContextMenu *pCM, HMENU hMenu, UINT CmdIdFirst, PCWSTR A UINT remove = FALSE; if (IsSelfShellVerb(Assoc, buf)) ++remove; - else if (!lstrcmpiW(L"cut", buf) || !lstrcmpiW(L"copy", buf) || !lstrcmpiW(L"link", buf)) + else if (!lstrcmpiW(L"cut", buf) || !lstrcmpiW(L"paste", buf) || !lstrcmpiW(L"pastelink", buf) || + !lstrcmpiW(L"delete", buf) || !lstrcmpiW(L"link", buf)) ++remove; if (remove && DeleteMenu(hMenu, i, MF_BYPOSITION)) @@ -133,10 +134,11 @@ die: HRESULT GetUIObjectOfPath(HWND hwnd, PCWSTR File, REFIID riid, void **ppv) { - HRESULT hr; + HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); IShellFolder *pSF; PCUITEMID_CHILD pidlItem; PIDLIST_ABSOLUTE pidl = ILCreateFromPath(File); + *ppv = NULL; if (pidl && SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pSF), &pidlItem))) { hr = IShellFolder_GetUIObjectOf(pSF, hwnd, 1, &pidlItem, riid, NULL, ppv); @@ -241,8 +243,14 @@ ShellExecuteVerb(HWND hwnd, PCWSTR Verb, PCWSTR File, BOOL Quit) } } +UINT +ErrorBox(HWND hwnd, UINT Error) +{ + return SHELL_ErrorBox(hwnd, Error); +} + void DisplayHelp(HWND hwnd) { - SHELL_ErrorBox(hwnd, ERROR_NOT_SUPPORTED); + ErrorBox(hwnd, ERROR_NOT_SUPPORTED); }