[ContextMenus]Update context menu labels and improve localization calls (#31000)

* Update file locksmith

* ImageResizer

* Update imageresizer context menu

* Changes

* [PowerRename]Don't use app name for context menu caption

* [ImageResizer]Apply string to old context menu too

* Add localization to the PowerRenameContextMenu project

* Show actual context menu string in PowerRename

* New Ids for the resource strings

* Add do not loc comments

* Add fallback and cache resource values

* Update src/modules/imageresizer/dll/ContextMenuHandler.cpp

Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>

* Remove do not loc comments from Image Resizer

---------

Co-authored-by: Jaime Bernardo <jaime@janeasystems.com>
Co-authored-by: Stefan Markovic <57057282+stefansjfw@users.noreply.github.com>
This commit is contained in:
Niels Laute
2024-01-25 13:27:25 +01:00
committed by GitHub
parent 43aa80fb1e
commit fc214a80c5
18 changed files with 194 additions and 50 deletions

View File

@@ -21,3 +21,4 @@ inline std::wstring get_resource_string(UINT resource_id, HINSTANCE instance, co
extern "C" IMAGE_DOS_HEADER __ImageBase;
// Wrapper for getting a string from the resource file. Returns the resource id text when fails.
#define GET_RESOURCE_STRING(resource_id) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), L#resource_id)
#define GET_RESOURCE_STRING_FALLBACK(resource_id, fallback) get_resource_string(resource_id, reinterpret_cast<HINSTANCE>(&__ImageBase), fallback)

View File

@@ -9,6 +9,7 @@
#include <common/themes/icon_helpers.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
// Implementations of inherited IUnknown methods
@@ -42,9 +43,7 @@ IFACEMETHODIMP_(ULONG) ExplorerCommand::Release()
IFACEMETHODIMP ExplorerCommand::GetTitle(IShellItemArray* psiItemArray, LPWSTR* ppszName)
{
WCHAR buffer[128];
LoadStringW(globals::instance, IDS_FILELOCKSMITH_COMMANDTITLE, buffer, ARRAYSIZE(buffer));
return SHStrDupW(buffer, ppszName);
return SHStrDup(context_menu_caption.c_str(), ppszName);
}
IFACEMETHODIMP ExplorerCommand::GetIcon(IShellItemArray* psiItemArray, LPWSTR* ppszIcon)
@@ -126,18 +125,15 @@ IFACEMETHODIMP ExplorerCommand::QueryContextMenu(HMENU hmenu, UINT indexMenu, UI
HRESULT hr = E_UNEXPECTED;
if (m_data_obj && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE)))
{
wchar_t menuName[128] = { 0 };
wcscpy_s(menuName, ARRAYSIZE(menuName), context_menu_caption.c_str());
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
mii.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_STATE;
mii.wID = idCmdFirst++;
mii.fType = MFT_STRING;
hr = GetTitle(NULL, &mii.dwTypeData);
if (FAILED(hr))
{
return hr;
}
mii.dwTypeData = (PWSTR)menuName;
mii.fState = MFS_ENABLED;
// icon from file
@@ -237,6 +233,7 @@ HRESULT ExplorerCommand::s_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void
ExplorerCommand::ExplorerCommand()
{
++globals::ref_count;
context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_FILELOCKSMITH_CONTEXT_MENU_ENTRY, L"Unlock with File Locksmith");
}
ExplorerCommand::~ExplorerCommand()

View File

@@ -49,4 +49,5 @@ private:
std::atomic<ULONG> m_ref_count = 1;
IDataObject* m_data_obj = NULL;
std::wstring context_menu_caption;
};

View File

@@ -117,9 +117,9 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="FileLocksmith_CommandTitle" xml:space="preserve">
<value>What's using this file?</value>
<comment>This text will be shown when the user opens the context menu (right clicks) a file.</comment>
<data name="FileLocksmith_Context_Menu_Entry" xml:space="preserve">
<value>Unlock with File Locksmith</value>
<comment>This text will be shown when the user opens the context menu (right clicks) a file. File Locksmith is the product name, do not loc.</comment>
</data>
<data name="FileLocksmith_PowerToyName" xml:space="preserve">
<value>File Locksmith</value>

View File

@@ -117,8 +117,8 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Resize_Pictures_Title" xml:space="preserve">
<value>Resize pictures</value>
<data name="ImageResizer_Context_Menu_Entry" xml:space="preserve">
<value>Resize with Image Resizer</value>
</data>
<data name="ImageResizer_App_Name" xml:space="preserve">
<value>Image Resizer</value>

View File

@@ -51,10 +51,7 @@ public:
// IExplorerCommand
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
{
wchar_t strResizePictures[64] = { 0 };
LoadString(g_hInst, IDS_RESIZE_PICTURES_TITLE, strResizePictures, ARRAYSIZE(strResizePictures));
return SHStrDup(strResizePictures, name);
return SHStrDup(context_menu_caption.c_str(), name);
}
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
@@ -272,7 +269,7 @@ private:
std::thread create_pipe_thread;
HANDLE hPipe = INVALID_HANDLE_VALUE;
std::wstring app_name = L"ImageResizer";
std::wstring context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_IMAGERESIZER_CONTEXT_MENU_ENTRY, L"Resize with Image Resizer");
};
CoCreatableClass(ImageResizerContextMenuCommand)

View File

@@ -18,7 +18,8 @@ CContextMenuHandler::CContextMenuHandler()
{
m_pidlFolder = NULL;
m_pdtobj = NULL;
app_name = GET_RESOURCE_STRING(IDS_RESIZE_PICTURES);
context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_IMAGERESIZER_CONTEXT_MENU_ENTRY, L"Resize with Image Resizer");
context_menu_caption_here = GET_RESOURCE_STRING_FALLBACK(IDS_IMAGERESIZER_CONTEXT_MENU_ENTRY_HERE, L"Resize with Image Resizer here");
}
CContextMenuHandler::~CContextMenuHandler()
@@ -105,22 +106,16 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu,
if (type == PERCEIVED_TYPE_IMAGE)
{
HRESULT hr = E_UNEXPECTED;
wchar_t strResizePictures[64] = { 0 };
wchar_t strResizePictures[128] = { 0 };
// If handling drag-and-drop...
if (m_pidlFolder)
{
// Suppressing C6031 warning since return value is not required.
#pragma warning(suppress : 6031)
// Load 'Resize pictures here' string
LoadString(g_hInst_imageResizer, IDS_RESIZE_PICTURES_HERE, strResizePictures, ARRAYSIZE(strResizePictures));
dragDropFlag = true;
dragDropFlag=true;
wcscpy_s(strResizePictures, ARRAYSIZE(strResizePictures), context_menu_caption_here.c_str());
}
else
{
// Suppressing C6031 warning since return value is not required.
#pragma warning(suppress : 6031)
// Load 'Resize pictures' string
LoadString(g_hInst_imageResizer, IDS_RESIZE_PICTURES, strResizePictures, ARRAYSIZE(strResizePictures));
wcscpy_s(strResizePictures, ARRAYSIZE(strResizePictures), context_menu_caption.c_str());
}
MENUITEMINFO mii;
@@ -348,7 +343,7 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
HRESULT __stdcall CContextMenuHandler::GetTitle(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszName)
{
return SHStrDup(app_name.c_str(), ppszName);
return SHStrDup(context_menu_caption.c_str(), ppszName);
}
HRESULT __stdcall CContextMenuHandler::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon)

View File

@@ -51,7 +51,8 @@ private:
PCIDLIST_ABSOLUTE m_pidlFolder;
IDataObject* m_pdtobj;
HBITMAP m_hbmpIcon = nullptr;
std::wstring app_name;
std::wstring context_menu_caption;
std::wstring context_menu_caption_here;
};
OBJECT_ENTRY_AUTO(__uuidof(ContextMenuHandler), CContextMenuHandler)

View File

@@ -117,11 +117,11 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="Resize_Pictures" xml:space="preserve">
<value>Resize pictures</value>
<data name="ImageResizer_Context_Menu_Entry" xml:space="preserve">
<value>Resize with Image Resizer</value>
</data>
<data name="Resize_Pictures_Here" xml:space="preserve">
<value>Resize pictures here</value>
<data name="ImageResizer_Context_Menu_Entry_Here" xml:space="preserve">
<value>Resize with Image Resizer here</value>
</data>
<data name="ImageResizer" xml:space="preserve">
<value>Image Resizer</value>

View File

@@ -1,6 +1,6 @@
#include <windows.h>
#include "resource.h"
#include "../../../common/version/version.h"
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"

View File

@@ -1,6 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.221104.6\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.221104.6\build\native\Microsoft.Windows.CppWinRT.props')" />
<Target Name="GenerateResourceFiles" BeforeTargets="PrepareForBuild">
<Exec Command="powershell -NonInteractive -executionpolicy Unrestricted $(SolutionDir)tools\build\convert-resx-to-rc.ps1 $(MSBuildThisFileDirectory) resource.base.h resource.h PowerRenameContextMenu.base.rc PowerRenameContextMenu.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
@@ -85,7 +88,8 @@ MakeAppx.exe pack /d . /p $(OutDir)PowerRenameContextMenuPackage.msix /nv</Comma
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.h" />
<None Include="resource.base.h" />
<ClInclude Include="Generated Files/resource.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
@@ -97,7 +101,8 @@ MakeAppx.exe pack /d . /p $(OutDir)PowerRenameContextMenuPackage.msix /nv</Comma
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PowerRenameContextMenu.rc" />
<ResourceCompile Include="Generated Files/PowerRenameContextMenu.rc" />
<None Include="PowerRenameContextMenu.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="Assets\PowerRename\**" CopyToOutputDirectory="PreserveNewest" />
@@ -110,6 +115,9 @@ MakeAppx.exe pack /d . /p $(OutDir)PowerRenameContextMenuPackage.msix /nv</Comma
<Project>{51920f1f-c28c-4adf-8660-4238766796c2}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="Resources.resx" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">

View File

@@ -21,7 +21,7 @@
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<ClInclude Include="Generated Files/resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
@@ -38,9 +38,18 @@
<None Include="Assets\PowerRename\**">
<Filter>Resource Files</Filter>
</None>
<None Include="resource.base.h">
<Filter>Header Files</Filter>
</None>
<None Include="PowerRenameContextMenu.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="Resources.resx">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="PowerRenameContextMenu.rc">
<ResourceCompile Include="Generated Files/PowerRenameContextMenu.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<data name="PowerRename_App_Name" xml:space="preserve">
<value>PowerRename</value>
<comment>do not loc, product name</comment>
</data>
<data name="PowerRename_Context_Menu_Entry" xml:space="preserve">
<value>Rename with PowerRename</value>
<comment>PowerRename is a product name. do not loc it</comment>
</data>
</root>

View File

@@ -16,8 +16,11 @@
#include <wrl/implements.h>
#include <wrl/client.h>
#include "Generated Files/resource.h"
#include <common/utils/elevation.h>
#include <common/utils/process_path.h>
#include <common/utils/resources.h>
#include <Helpers.h>
#include <Settings.h>
#include <trace.h>
@@ -60,7 +63,7 @@ public:
// IExplorerCommand
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
{
return SHStrDup(app_name.c_str(), name);
return SHStrDup(context_menu_caption.c_str(), name);
}
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
@@ -261,7 +264,7 @@ private:
std::thread create_pipe_thread;
HANDLE hPipe = INVALID_HANDLE_VALUE;
std::wstring app_name = L"PowerRename";
std::wstring context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_POWERRENAME_CONTEXT_MENU_ENTRY, L"Rename with PowerRename");
};
CoCreatableClass(PowerRenameContextMenuCommand)

View File

@@ -22,7 +22,7 @@ struct InvokeStruct
CPowerRenameMenu::CPowerRenameMenu()
{
ModuleAddRef();
app_name = GET_RESOURCE_STRING(IDS_POWERRENAME_APP_NAME);
context_menu_caption = GET_RESOURCE_STRING_FALLBACK(IDS_POWERRENAME_CONTEXT_MENU_ENTRY, L"Rename with PowerRename");
}
CPowerRenameMenu::~CPowerRenameMenu()
@@ -87,8 +87,8 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
HRESULT hr = E_UNEXPECTED;
if (m_spdo && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE)))
{
wchar_t menuName[64] = { 0 };
LoadString(g_hInst, IDS_POWERRENAME, menuName, ARRAYSIZE(menuName));
wchar_t menuName[128] = { 0 };
wcscpy_s(menuName, ARRAYSIZE(menuName), context_menu_caption.c_str());
MENUITEMINFO mii;
mii.cbSize = sizeof(MENUITEMINFO);
@@ -251,7 +251,7 @@ HRESULT CPowerRenameMenu::RunPowerRename(CMINVOKECOMMANDINFO* pici, IShellItemAr
HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszName)
{
return SHStrDup(app_name.c_str(), ppszName);
return SHStrDup(context_menu_caption.c_str(), ppszName);
}
HRESULT __stdcall CPowerRenameMenu::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon)

View File

@@ -72,5 +72,5 @@ private:
std::atomic<long> m_refCount = 1;
HBITMAP m_hbmpIcon = nullptr;
CComPtr<IDataObject> m_spdo;
std::wstring app_name;
std::wstring context_menu_caption;
};

View File

@@ -125,6 +125,10 @@
<value>PowerRename</value>
<comment>do not loc, product name</comment>
</data>
<data name="PowerRename_Context_Menu_Entry" xml:space="preserve">
<value>Rename with PowerRename</value>
<comment>PowerRename is a product name. do not loc it</comment>
</data>
<data name="Settings_Description" xml:space="preserve">
<value>A Windows Shell extension for more advanced bulk renaming using search and replace or regular expressions.</value>
</data>