mirror of
https://github.com/reactos/reactos.git
synced 2025-06-05 09:20:30 +00:00
[SHIMGVW] Display shell context menu for the image on right-click (#7711)
CORE-13340
This commit is contained in:
parent
329a414584
commit
bc52d5f1f4
6 changed files with 198 additions and 19 deletions
|
@ -7,6 +7,7 @@ list(APPEND SOURCE
|
|||
shimgvw.c
|
||||
comsup.c
|
||||
shimgvw.rc
|
||||
util.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shimgvw_stubs.c
|
||||
${CMAKE_CURRENT_BINARY_DIR}/shimgvw.def)
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ static HRESULT Read(HANDLE hFile, void* Buffer, DWORD Size)
|
|||
return Size == Transferred ? S_OK : HResultFromWin32(ERROR_HANDLE_EOF);
|
||||
}
|
||||
|
||||
struct IMAGEINFO
|
||||
struct IMAGESTATS
|
||||
{
|
||||
UINT w, h;
|
||||
BYTE bpp;
|
||||
|
@ -91,7 +91,7 @@ static BYTE GetPngBppFromIHDRData(const void* buffer)
|
|||
return (depth <= 16 && type <= 6) ? channels[type] * depth : 0;
|
||||
}
|
||||
|
||||
static bool GetInfoFromPng(const void* file, SIZE_T size, IMAGEINFO& info)
|
||||
static bool GetInfoFromPng(const void* file, SIZE_T size, IMAGESTATS& info)
|
||||
{
|
||||
C_ASSERT(sizeof(PNGSIGNATURE) == 8);
|
||||
C_ASSERT(sizeof(PNGSIGANDIHDR) == 8 + (4 + 4 + (4 + 4 + 5) + 4));
|
||||
|
@ -111,7 +111,7 @@ static bool GetInfoFromPng(const void* file, SIZE_T size, IMAGEINFO& info)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool GetInfoFromBmp(const void* pBitmapInfo, IMAGEINFO& info)
|
||||
static bool GetInfoFromBmp(const void* pBitmapInfo, IMAGESTATS& info)
|
||||
{
|
||||
BitmapInfoHeader bih(pBitmapInfo);
|
||||
info.w = bih.biWidth;
|
||||
|
@ -121,11 +121,11 @@ static bool GetInfoFromBmp(const void* pBitmapInfo, IMAGEINFO& info)
|
|||
return info.w && bpp == info.bpp;
|
||||
}
|
||||
|
||||
static bool GetInfoFromIcoBmp(const void* pBitmapInfo, IMAGEINFO& stat)
|
||||
static bool GetInfoFromIcoBmp(const void* pBitmapInfo, IMAGESTATS& info)
|
||||
{
|
||||
bool ret = GetInfoFromBmp(pBitmapInfo, stat);
|
||||
stat.h /= 2; // Don't include mask
|
||||
return ret && stat.h;
|
||||
bool ret = GetInfoFromBmp(pBitmapInfo, info);
|
||||
info.h /= 2; // Don't include mask
|
||||
return ret && info.h;
|
||||
}
|
||||
|
||||
EXTERN_C PCWSTR GetExtraExtensionsGdipList(VOID)
|
||||
|
@ -158,7 +158,7 @@ static void OverrideFileContent(HGLOBAL& hMem, DWORD& Size)
|
|||
for (UINT i = 0; i < count; ++i)
|
||||
{
|
||||
BOOL valid = FALSE;
|
||||
IMAGEINFO info;
|
||||
IMAGESTATS info;
|
||||
const BYTE* data = buffer + entries[i].offset;
|
||||
if (IsPngSignature(data, entries[i].size))
|
||||
valid = GetInfoFromPng(data, entries[i].size, info);
|
||||
|
@ -189,7 +189,7 @@ static void OverrideFileContent(HGLOBAL& hMem, DWORD& Size)
|
|||
const BYTE* data = buffer + entries[i].offset;
|
||||
if (IsPngSignature(data, entries[i].size))
|
||||
{
|
||||
IMAGEINFO info;
|
||||
IMAGESTATS info;
|
||||
if (!GetInfoFromPng(data, entries[i].size, info))
|
||||
continue;
|
||||
bih.biPlanes = 1;
|
||||
|
|
|
@ -1190,7 +1190,7 @@ ZoomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
case WM_RBUTTONUP:
|
||||
{
|
||||
ZoomWnd_OnButtonUp(hwnd, uMsg, wParam, lParam);
|
||||
break;
|
||||
goto doDefault;
|
||||
}
|
||||
case WM_LBUTTONDBLCLK:
|
||||
{
|
||||
|
@ -1209,6 +1209,10 @@ ZoomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
(SHORT)HIWORD(wParam), (UINT)LOWORD(wParam));
|
||||
break;
|
||||
}
|
||||
case WM_CONTEXTMENU:
|
||||
if (Preview_IsMainWnd(pData->m_hwnd))
|
||||
DoShellContextMenuOnFile(hwnd, pData->m_szFile, lParam);
|
||||
break;
|
||||
case WM_HSCROLL:
|
||||
case WM_VSCROLL:
|
||||
ZoomWnd_OnHVScroll(pData, hwnd, wParam, uMsg == WM_VSCROLL);
|
||||
|
@ -1230,7 +1234,7 @@ ZoomWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
default: doDefault:
|
||||
{
|
||||
return DefWindowProcW(hwnd, uMsg, wParam, lParam);
|
||||
}
|
||||
|
@ -1429,9 +1433,7 @@ Preview_ToggleSlideShowEx(PPREVIEW_DATA pData, BOOL StartTimer)
|
|||
|
||||
if (IsWindowVisible(g_hwndFullscreen))
|
||||
{
|
||||
KillTimer(g_hwndFullscreen, SLIDESHOW_TIMER_ID);
|
||||
ShowWindow(g_hMainWnd, SW_SHOW);
|
||||
ShowWindow(g_hwndFullscreen, SW_HIDE);
|
||||
Preview_EndSlideShow(g_hwndFullscreen);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1577,6 +1579,10 @@ Preview_OnCommand(HWND hwnd, UINT nCommandID)
|
|||
Preview_Edit(hwnd);
|
||||
break;
|
||||
|
||||
case IDC_HELP_TOC:
|
||||
DisplayHelp(hwnd);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1693,6 +1699,13 @@ PreviewWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|||
Preview_OnDestroy(hwnd);
|
||||
break;
|
||||
}
|
||||
case WM_CONTEXTMENU:
|
||||
{
|
||||
PPREVIEW_DATA pData = Preview_GetData(hwnd);
|
||||
if ((int)lParam == -1)
|
||||
return ZoomWndProc(pData->m_hwndZoom, uMsg, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
case WM_TIMER:
|
||||
{
|
||||
if (wParam == SLIDESHOW_TIMER_ID)
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#define _INC_WINDOWS
|
||||
#define COM_NO_WINDOWS_H
|
||||
#define INITGUID
|
||||
#define COBJMACROS
|
||||
|
||||
#include <windef.h>
|
||||
#include <winbase.h>
|
||||
|
@ -23,6 +24,7 @@
|
|||
#include <gdiplus.h>
|
||||
#include <shlwapi.h>
|
||||
#include <strsafe.h>
|
||||
#include <shobjidl.h>
|
||||
|
||||
#include <debug.h>
|
||||
|
||||
|
@ -69,6 +71,9 @@ void Anime_Start(PANIME pAnime, DWORD dwDelay);
|
|||
void Anime_Pause(PANIME pAnime);
|
||||
BOOL Anime_OnTimer(PANIME pAnime, WPARAM wParam);
|
||||
|
||||
void DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam);
|
||||
void DisplayHelp(HWND hwnd);
|
||||
|
||||
static inline LPVOID QuickAlloc(SIZE_T cbSize, BOOL bZero)
|
||||
{
|
||||
return HeapAlloc(GetProcessHeap(), (bZero ? HEAP_ZERO_MEMORY : 0), cbSize);
|
||||
|
|
158
dll/win32/shimgvw/util.c
Normal file
158
dll/win32/shimgvw/util.c
Normal file
|
@ -0,0 +1,158 @@
|
|||
/*
|
||||
* PROJECT: ReactOS Picture and Fax Viewer
|
||||
* LICENSE: GPL-2.0 (https://spdx.org/licenses/GPL-2.0)
|
||||
* PURPOSE: Utility routines
|
||||
* COPYRIGHT: Copyright 2025 Whindmar Saksit <whindsaks@proton.me>
|
||||
*/
|
||||
|
||||
#include "shimgvw.h"
|
||||
#include <windowsx.h>
|
||||
#include <shlobj.h>
|
||||
#include <shellapi.h>
|
||||
#include <shellutils.h>
|
||||
#include <shlwapi_undoc.h>
|
||||
|
||||
IContextMenu *g_pContextMenu = NULL;
|
||||
|
||||
static int
|
||||
GetMenuItemIdByPos(HMENU hMenu, UINT Pos)
|
||||
{
|
||||
MENUITEMINFOW mii;
|
||||
mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); /* USER32 version agnostic */
|
||||
mii.fMask = MIIM_ID;
|
||||
mii.cch = 0;
|
||||
return GetMenuItemInfoW(hMenu, Pos, TRUE, &mii) ? mii.wID : -1;
|
||||
}
|
||||
|
||||
static BOOL
|
||||
IsMenuSeparator(HMENU hMenu, UINT Pos)
|
||||
{
|
||||
MENUITEMINFOW mii;
|
||||
mii.cbSize = FIELD_OFFSET(MENUITEMINFOW, hbmpItem); /* USER32 version agnostic */
|
||||
mii.fMask = MIIM_FTYPE;
|
||||
mii.cch = 0;
|
||||
return GetMenuItemInfoW(hMenu, Pos, TRUE, &mii) && (mii.fType & MFT_SEPARATOR);
|
||||
}
|
||||
|
||||
static BOOL
|
||||
IsSelfShellVerb(PCWSTR Assoc, PCWSTR Verb)
|
||||
{
|
||||
WCHAR buf[MAX_PATH * 3];
|
||||
DWORD cch = _countof(buf);
|
||||
HRESULT hr = AssocQueryStringW(ASSOCF_NOTRUNCATE, ASSOCSTR_COMMAND, Assoc, Verb, buf, &cch);
|
||||
return hr == S_OK && *Assoc == L'.' && StrStrW(buf, L",ImageView_Fullscreen");
|
||||
}
|
||||
|
||||
static void
|
||||
ModifyShellContextMenu(IContextMenu *pCM, HMENU hMenu, UINT CmdIdFirst, PCWSTR Assoc)
|
||||
{
|
||||
HRESULT hr;
|
||||
UINT id, i;
|
||||
for (i = 0; i < GetMenuItemCount(hMenu); ++i)
|
||||
{
|
||||
WCHAR buf[200];
|
||||
id = GetMenuItemIdByPos(hMenu, i);
|
||||
if (id == (UINT)-1)
|
||||
continue;
|
||||
|
||||
*buf = UNICODE_NULL;
|
||||
/* Note: We just ask for the wide string because all the items we care about come from shell32 and it handles both */
|
||||
hr = IContextMenu_GetCommandString(pCM, id - CmdIdFirst, GCS_VERBW, NULL, (char*)buf, _countof(buf));
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
UINT remove = FALSE;
|
||||
if (IsSelfShellVerb(Assoc, buf))
|
||||
++remove;
|
||||
else if (!lstrcmpiW(L"cut", buf) || !lstrcmpiW(L"copy", buf) || !lstrcmpiW(L"link", buf))
|
||||
++remove;
|
||||
|
||||
if (remove && DeleteMenu(hMenu, i, MF_BYPOSITION))
|
||||
{
|
||||
if (i-- > 0)
|
||||
{
|
||||
if (IsMenuSeparator(hMenu, i) && IsMenuSeparator(hMenu, i + 1))
|
||||
DeleteMenu(hMenu, i, MF_BYPOSITION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (IsMenuSeparator(hMenu, 0) && DeleteMenu(hMenu, 0, MF_BYPOSITION)) {}
|
||||
}
|
||||
|
||||
static LRESULT CALLBACK
|
||||
ShellContextMenuWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
LRESULT lRes = 0;
|
||||
if (FAILED(SHForwardContextMenuMsg((IUnknown*)g_pContextMenu, uMsg, wParam, lParam, &lRes, TRUE)))
|
||||
lRes = DefWindowProc(hwnd, uMsg, wParam, lParam);
|
||||
return lRes;
|
||||
}
|
||||
|
||||
static void
|
||||
DoShellContextMenu(HWND hwnd, IContextMenu *pCM, PCWSTR File, LPARAM lParam)
|
||||
{
|
||||
enum { first = 1, last = 0x7fff };
|
||||
HRESULT hr;
|
||||
HMENU hMenu = CreatePopupMenu();
|
||||
UINT cmf = GetKeyState(VK_SHIFT) < 0 ? CMF_EXTENDEDVERBS : 0;
|
||||
|
||||
POINT pt = { GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam) };
|
||||
if ((int)lParam == -1)
|
||||
{
|
||||
RECT rect;
|
||||
GetWindowRect(hwnd, &rect);
|
||||
pt.x = (rect.left + rect.right) / 2;
|
||||
pt.y = rect.top;
|
||||
}
|
||||
|
||||
g_pContextMenu = pCM;
|
||||
hwnd = SHCreateWorkerWindowW(ShellContextMenuWindowProc, hwnd, 0, WS_VISIBLE | WS_CHILD, NULL, 0);
|
||||
if (!hwnd)
|
||||
goto die;
|
||||
hr = IContextMenu_QueryContextMenu(pCM, hMenu, 0, first, last, cmf | CMF_NODEFAULT);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
UINT id;
|
||||
ModifyShellContextMenu(pCM, hMenu, first, PathFindExtensionW(File));
|
||||
id = TrackPopupMenuEx(hMenu, TPM_RETURNCMD, pt.x, pt.y, hwnd, NULL);
|
||||
if (id)
|
||||
{
|
||||
UINT flags = (GetKeyState(VK_SHIFT) < 0 ? CMIC_MASK_SHIFT_DOWN : 0) |
|
||||
(GetKeyState(VK_CONTROL) < 0 ? CMIC_MASK_CONTROL_DOWN : 0);
|
||||
CMINVOKECOMMANDINFO ici = { sizeof(ici), flags, hwnd, MAKEINTRESOURCEA(id - first) };
|
||||
ici.nShow = SW_SHOW;
|
||||
hr = IContextMenu_InvokeCommand(pCM, &ici);
|
||||
}
|
||||
}
|
||||
DestroyWindow(hwnd);
|
||||
die:
|
||||
DestroyMenu(hMenu);
|
||||
g_pContextMenu = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
DoShellContextMenuOnFile(HWND hwnd, PCWSTR File, LPARAM lParam)
|
||||
{
|
||||
HRESULT hr;
|
||||
IShellFolder *pSF;
|
||||
PCUITEMID_CHILD pidlItem;
|
||||
PIDLIST_ABSOLUTE pidl = ILCreateFromPath(File);
|
||||
if (pidl && SUCCEEDED(SHBindToParent(pidl, IID_PPV_ARG(IShellFolder, &pSF), &pidlItem)))
|
||||
{
|
||||
IContextMenu *pCM;
|
||||
hr = IShellFolder_GetUIObjectOf(pSF, hwnd, 1, &pidlItem, &IID_IContextMenu, NULL, (void**)&pCM);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
DoShellContextMenu(hwnd, pCM, File, lParam);
|
||||
IContextMenu_Release(pCM);
|
||||
}
|
||||
IShellFolder_Release(pSF);
|
||||
}
|
||||
SHFree(pidl);
|
||||
}
|
||||
|
||||
void DisplayHelp(HWND hwnd)
|
||||
{
|
||||
SHELL_ErrorBox(hwnd, ERROR_NOT_SUPPORTED);
|
||||
}
|
|
@ -23,7 +23,7 @@
|
|||
extern "C" {
|
||||
#endif /* defined(__cplusplus) */
|
||||
|
||||
inline ULONG
|
||||
static inline ULONG
|
||||
Win32DbgPrint(const char *filename, int line, const char *lpFormat, ...)
|
||||
{
|
||||
char szMsg[512];
|
||||
|
@ -63,11 +63,11 @@ Win32DbgPrint(const char *filename, int line, const char *lpFormat, ...)
|
|||
# define IID_PPV_ARG(Itype, ppType) IID_##Itype, reinterpret_cast<void**>((static_cast<Itype**>(ppType)))
|
||||
# define IID_NULL_PPV_ARG(Itype, ppType) IID_##Itype, NULL, reinterpret_cast<void**>((static_cast<Itype**>(ppType)))
|
||||
#else
|
||||
# define IID_PPV_ARG(Itype, ppType) IID_##Itype, (void**)(ppType)
|
||||
# define IID_NULL_PPV_ARG(Itype, ppType) IID_##Itype, NULL, (void**)(ppType)
|
||||
# define IID_PPV_ARG(Itype, ppType) &IID_##Itype, (void**)(ppType)
|
||||
# define IID_NULL_PPV_ARG(Itype, ppType) &IID_##Itype, NULL, (void**)(ppType)
|
||||
#endif
|
||||
|
||||
inline HRESULT HResultFromWin32(DWORD hr)
|
||||
static inline HRESULT HResultFromWin32(DWORD hr)
|
||||
{
|
||||
// HRESULT_FROM_WIN32 will evaluate its parameter twice, this function will not.
|
||||
return HRESULT_FROM_WIN32(hr);
|
||||
|
@ -75,7 +75,7 @@ inline HRESULT HResultFromWin32(DWORD hr)
|
|||
|
||||
#if 1
|
||||
|
||||
inline BOOL _ROS_FAILED_HELPER(HRESULT hr, const char* expr, const char* filename, int line)
|
||||
static inline BOOL _ROS_FAILED_HELPER(HRESULT hr, const char* expr, const char* filename, int line)
|
||||
{
|
||||
if (FAILED(hr))
|
||||
{
|
||||
|
@ -122,6 +122,8 @@ SHELL_ErrorBox(H hwndOwner, UINT Error = GetLastError())
|
|||
{
|
||||
return SHELL_ErrorBoxHelper(const_cast<HWND>(hwndOwner), Error);
|
||||
}
|
||||
#else
|
||||
#define SHELL_ErrorBox SHELL_ErrorBoxHelper
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
|
Loading…
Reference in a new issue