[New+]Windows 10 support (#35832)

This commit is contained in:
Christian Gaarden Gaardmark
2024-11-27 06:22:05 -08:00
committed by GitHub
parent 08fe8089b8
commit 084402a2bd
32 changed files with 1246 additions and 109 deletions

View File

@@ -997,6 +997,7 @@ newitem
newpath newpath
newplus newplus
NEWPLUSCONTEXTMENU NEWPLUSCONTEXTMENU
NEWPLUSSHELLEXTENSIONWIN
newrow newrow
newsgroups newsgroups
NIF NIF
@@ -1168,6 +1169,7 @@ pnid
Pnp Pnp
Popups Popups
POPUPWINDOW POPUPWINDOW
POSITIONITEM
POWERRENAMECONTEXTMENU POWERRENAMECONTEXTMENU
powerrenameinput powerrenameinput
POWERRENAMETEST POWERRENAMETEST

View File

@@ -181,6 +181,7 @@
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll", "WinUI3Apps\\PowerToys.NewPlus.ShellExtension.dll",
"WinUI3Apps\\NewPlusPackage.msix", "WinUI3Apps\\NewPlusPackage.msix",
"WinUI3Apps\\PowerToys.NewPlus.ShellExtension.win10.dll",
"PowerAccent.Core.dll", "PowerAccent.Core.dll",
"PowerToys.PowerAccent.dll", "PowerToys.PowerAccent.dll",

View File

@@ -632,6 +632,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MouseWithoutBorders.UnitTes
EndProject EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}" Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkspacesCsharpLibrary", "src\modules\Workspaces\WorkspacesCsharpLibrary\WorkspacesCsharpLibrary.csproj", "{89D0E199-B17A-418C-B2F8-7375B6708357}"
EndProject EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NewPlus.ShellExtension.win10", "src\modules\NewPlus\NewShellExtensionContextMenu.win10\NewPlus.ShellExtension.win10.vcxproj", "{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|ARM64 = Debug|ARM64 Debug|ARM64 = Debug|ARM64
@@ -2792,6 +2794,18 @@ Global
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64 {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x64.Build.0 = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64 {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.ActiveCfg = Release|x64
{89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64 {89D0E199-B17A-418C-B2F8-7375B6708357}.Release|x86.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.ActiveCfg = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|ARM64.Build.0 = Debug|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x64.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.ActiveCfg = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Debug|x86.Build.0 = Debug|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.ActiveCfg = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|ARM64.Build.0 = Release|ARM64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x64.Build.0 = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.ActiveCfg = Release|x64
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E}.Release|x86.Build.0 = Release|x64
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE
@@ -3024,6 +3038,7 @@ Global
{8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216} {8F021B46-362B-485C-BFBA-CCF83E820CBD} = {8F62026A-294B-41C6-8839-87463613F216}
{66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC} {66614C26-314C-4B91-9071-76133422CFEF} = {B6C42F16-73EB-477E-8B0D-4E6CF6C20AAC}
{89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF} {89D0E199-B17A-418C-B2F8-7375B6708357} = {A2221D7E-55E7-4BEA-90D1-4F162D670BBF}
{0DB0F63A-D2F8-4DA3-A650-2D0B8724218E} = {CA716AE6-FE5C-40AC-BB8F-2C87912687AC}
EndGlobalSection EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0} SolutionGuid = {C3A2F9D1-7930-4EF4-A6FC-7EE0A99821D0}

View File

@@ -18,6 +18,19 @@
<DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)"> <DirectoryRef Id="NewPlusAssetsInstallFolder" FileSource="$(var.NewPlusAssetsFilesPath)">
<!-- Generated by generateFileComponents.ps1 --> <!-- Generated by generateFileComponents.ps1 -->
<!--NewPlusAssetsFiles_Component_Def--> <!--NewPlusAssetsFiles_Component_Def-->
<!-- NewPlus Shell Extension for Win10 registration -->
<Component Id="NewPlus_ShellExtension_win10" Guid="D5456D4A-6EEC-4B85-944D-6A6A4A74FFA6" Win64="yes">
<RegistryKey Root="$(var.RegistryScope)" Key="Software\Classes\CLSID\{FF90D477-E32A-4BE8-8CC5-A502A97F5401}">
<RegistryValue Type="string" Value="NewPlus Shell Extension Win10" />
<RegistryValue Type="string" Name="ContextMenuOptIn" Value="" />
<RegistryValue Type="string" Key="InprocServer32" Value="[WinUI3AppsInstallFolder]PowerToys.NewPlus.ShellExtension.win10.dll" />
<RegistryValue Type="string" Key="InprocServer32" Name="ThreadingModel" Value="Apartment" />
</RegistryKey>
<RegistryKey Root="$(var.RegistryScope)" Key="SOFTWARE\Classes\Directory\background\ShellEx\ContextMenuHandlers\NewPlusShellExtensionWin10">
<RegistryValue Type="string" Value="{FF90D477-E32A-4BE8-8CC5-A502A97F5401}"/>
</RegistryKey>
</Component>
</DirectoryRef> </DirectoryRef>
<ComponentGroup Id="NewPlusComponentGroup"> <ComponentGroup Id="NewPlusComponentGroup">
@@ -27,6 +40,7 @@
</RegistryKey> </RegistryKey>
<RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/> <RemoveFolder Id="RemoveFolderNewPlusAssetsFolder" Directory="NewPlusAssetsInstallFolder" On="uninstall"/>
</Component> </Component>
<ComponentRef Id="NewPlus_ShellExtension_win10" />
</ComponentGroup> </ComponentGroup>

View File

@@ -0,0 +1,152 @@
<?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.240111.5\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\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 new.base.rc new.rc" />
</Target>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0db0f63a-d2f8-4da3-a650-2d0b8724218e}</ProjectGuid>
<RootNamespace>NewPlusShellExtensionWin10</RootNamespace>
<WindowsTargetPlatformVersion>10.0.22621.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
<UseDebugLibraries>true</UseDebugLibraries>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
<UseDebugLibraries>false</UseDebugLibraries>
<WholeProgramOptimization>true</WholeProgramOptimization>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup>
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
<TargetName>PowerToys.NewPlus.ShellExtension.win10</TargetName>
<LinkIncremental />
<IgnoreImportLibrary />
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<IntrinsicFunctions>true</IntrinsicFunctions>
<FunctionLevelLinking>true</FunctionLevelLinking>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;NEWPLUSSHELLEXTENSIONWIN10_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories);..\NewShellExtensionContextMenu</AdditionalIncludeDirectories>
<CompileAsWinRT>false</CompileAsWinRT>
<LanguageStandard>stdcpplatest</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h" />
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h" />
<ClInclude Include="dll_main.h" />
<ClInclude Include="Generated Files\resource.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="resource.base.h" />
<ClInclude Include="shell_context_menu_win10.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp" />
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp" />
<ClCompile Include="dll_main.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader>Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="shell_context_menu_win10.cpp" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\new.rc" />
<None Include="new.base.rc" />
</ItemGroup>
<ItemGroup>
<None Include="resources.resx">
<SubType>Designer</SubType>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Telemetry\EtwTrace\EtwTrace.vcxproj">
<Project>{8f021b46-362b-485c-bfba-ccf83e820cbd}</Project>
</ProjectReference>
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<None Include="dll.def" />
<None Include="packages.config" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<Import Project="..\..\..\..\deps\spdlog.props" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" />
<Import Project="..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets" Condition="Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.CppWinRT.2.0.240111.5\build\native\Microsoft.Windows.CppWinRT.targets'))" />
<Error Condition="!Exists('..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\Microsoft.Windows.ImplementationLibrary.1.0.231216.1\build\native\Microsoft.Windows.ImplementationLibrary.targets'))" />
</Target>
</Project>

View File

@@ -0,0 +1,116 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
<Filter Include="Generated Files">
<UniqueIdentifier>{4cea4fff-ccef-4b62-9e46-f33da2b9a0cc}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="pch.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="shell_context_menu_win10.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.base.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Generated Files\resource.h">
<Filter>Generated Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\template_folder.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\template_item.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\trace.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="dll_main.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="pch.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="dll_main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="shell_context_menu_win10.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\template_item.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\template_folder.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\shell_context_sub_menu_item.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\trace.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Generated Files\new.rc">
<Filter>Generated Files</Filter>
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
<None Include="dll.def">
<Filter>Source Files</Filter>
</None>
<None Include="new.base.rc">
<Filter>Resource Files</Filter>
</None>
<None Include="resources.resx">
<Filter>Resource Files</Filter>
</None>
</ItemGroup>
<ItemGroup>
<Natvis Include="$(MSBuildThisFileDirectory)..\..\natvis\wil.natvis" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,6 @@
LIBRARY
EXPORTS
DllGetClassObject PRIVATE
DllCanUnloadNow PRIVATE
DllGetActivationFactory PRIVATE

View File

@@ -0,0 +1,44 @@
#include "pch.h"
#include "shell_context_menu_win10.h"
#include "dll_main.h"
#include "trace.h"
#include <common/Telemetry/EtwTrace/EtwTrace.h>
HMODULE module_instance_handle = 0;
Shared::Trace::ETWTrace trace(L"NewPlusShellExtension_Win10");
BOOL APIENTRY DllMain(HMODULE module_handle, DWORD ul_reason_for_call, LPVOID reserved)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
module_instance_handle = module_handle;
Trace::RegisterProvider();
newplus::utilities::init_logger();
break;
case DLL_PROCESS_DETACH:
Trace::UnregisterProvider();
break;
}
return TRUE;
}
STDAPI DllGetActivationFactory(_In_ HSTRING activatableClassId, _COM_Outptr_ IActivationFactory** factory)
{
return Module<ModuleType::InProc>::GetModule().GetActivationFactory(activatableClassId, factory);
}
STDAPI DllCanUnloadNow()
{
return Module<InProc>::GetModule().GetObjectCount() == 0 ? S_OK : S_FALSE;
}
STDAPI DllGetClassObject(_In_ REFCLSID ref_class_id, _In_ REFIID ref_interface_id, _Outptr_ LPVOID FAR* object)
{
return Module<InProc>::GetModule().GetClassObject(ref_class_id, ref_interface_id, object);
}
CoCreatableClass(shell_context_menu_win10)

View File

@@ -0,0 +1,6 @@
#pragma once
#include <common/Telemetry/EtwTrace/EtwTrace.h>
extern HMODULE module_instance_handle;
extern Shared::Trace::ETWTrace trace;

View File

@@ -0,0 +1,54 @@
#include <windows.h>
#include "Generated Files/resource.h"
#include "../../../../common/version/version.h"
#define APSTUDIO_READONLY_SYMBOLS
#include "winres.h"
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x2L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", COMPANY_NAME
VALUE "FileDescription", FILE_DESCRIPTION
VALUE "FileVersion", FILE_VERSION_STRING
VALUE "InternalName", INTERNAL_NAME
VALUE "LegalCopyright", COPYRIGHT_NOTE
VALUE "OriginalFilename", ORIGINAL_FILENAME
VALUE "ProductName", PRODUCT_NAME
VALUE "ProductVersion", PRODUCT_VERSION_STRING
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Microsoft.Windows.CppWinRT" version="2.0.240111.5" targetFramework="native" />
<package id="Microsoft.Windows.ImplementationLibrary" version="1.0.231216.1" targetFramework="native" />
</packages>

View File

@@ -0,0 +1,3 @@
// pch.cpp: source file corresponding to the pre-compiled header
#include "pch.h"

View File

@@ -0,0 +1,39 @@
// Precompiled header file.
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMCX
#define NOHELP
#define NOCOMM
// Windows and STL
#include <Shobjidl.h>
#include <shlwapi.h>
#include <shellapi.h>
#include <Windows.h>
#include <shlobj.h>
#include <vector>
#include <system_error>
#include <memory>
#include <iostream>
#include <atlbase.h>
#include <wrl.h>
#include <wrl/module.h>
#include <wrl/client.h>
#include <unknwn.h>
using namespace Microsoft::WRL;
// PowerToys project common
#include <ProjectTelemetry.h>
#include <common/utils/resources.h>
#include <common/logger/logger.h>
#include <common/logger/logger_settings.h>
#include <common/utils/logger_helper.h>
#include <common/Themes/theme_helpers.h>
// New project specific
#include "dll_main.h"
#include "template_folder.h"
#include "settings.h"
#include "new_utilities.h"

View File

@@ -0,0 +1,12 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
//////////////////////////////
// Non-localizable
#define FILE_DESCRIPTION "PowerToys.New+"
#define INTERNAL_NAME "PowerToys.New+"
#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.win10.dll"
// Non-localizable
//////////////////////////////

View File

@@ -0,0 +1,132 @@
<?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="context_menu_item_new" xml:space="preserve">
<value>New+</value>
<comment>The main context menu item that users click on. This should be localized to match New in Windows. e.g. Danish it would become Ny+</comment>
</data>
<data name="context_menu_item_open_templates" xml:space="preserve">
<value>Open templates</value>
<comment>The menu item in the context menu that enables user to open the folder that contains their templates.</comment>
</data>
<data name="default_template_sub_folder_name_where_templates_are_stored" xml:space="preserve">
<value>Templates</value>
<comment>Default subfolder name where templates are stored.</comment>
</data>
</root>

View File

@@ -0,0 +1,270 @@
#include "pch.h"
#include "shell_context_menu_win10.h"
#include "shell_context_sub_menu.h"
#include "shell_context_sub_menu_item.h"
#include "new_utilities.h"
#include "settings.h"
#include "trace.h"
#include "Generated Files/resource.h"
#include <common/Themes/icon_helpers.h>
#include <common/utils/winapi_error.h>
using namespace Microsoft::WRL;
using namespace newplus;
shell_context_menu_win10::~shell_context_menu_win10()
{
for (const auto& handle : bitmap_handles)
{
DeleteObject(handle);
}
}
#pragma region IShellExtInit
IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObject*, HKEY)
{
return S_OK;
}
#pragma endregion
#pragma region IContextMenu
IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags)
{
if (!NewSettingsInstance().GetEnabled()
|| package::IsWin11OrGreater()
)
{
return E_FAIL;
}
if (menu_flags & CMF_DEFAULTONLY)
{
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0);
}
try
{
// Create the initial context popup menu containing the list of templates and open templates action
int menu_id = menu_first_cmd_id;
MENUITEMINFO newplus_main_context_menu_item;
HMENU sub_menu_of_templates = CreatePopupMenu();
int sub_menu_index = 0;
// Determine the New+ Template folder location
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
// Create the New+ Template folder location if it doesn't exist (very rare scenario)
utilities::create_folder_if_not_exist(template_folder_root);
// Scan the folder for any files and folders (the templates)
templates = new template_folder(template_folder_root);
templates->rescan_template_folder();
const auto number_of_templates = templates->list_of_templates.size();
// Create the New+ menu item and point to the initial context popup menu
static const std::wstring localized_context_menu_item =
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
wchar_t newplus_menu_name[20] = { 0 };
wcscpy_s(newplus_menu_name, ARRAYSIZE(newplus_menu_name), localized_context_menu_item.c_str());
newplus_main_context_menu_item.cbSize = sizeof(MENUITEMINFOW);
newplus_main_context_menu_item.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_SUBMENU;
newplus_main_context_menu_item.wID = menu_id;
newplus_main_context_menu_item.fType = MFT_STRING;
newplus_main_context_menu_item.dwTypeData = (PWSTR)newplus_menu_name;
newplus_main_context_menu_item.hSubMenu = sub_menu_of_templates;
const auto newplus_icon_index = 0;
if (bitmap_handles.size() == 0)
{
const std::wstring icon_file = utilities::get_new_icon_resource_filepath(
module_instance_handle, ThemeHelpers::GetAppTheme())
.c_str();
HICON local_icon_handle = static_cast<HICON>(
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
if (local_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(local_icon_handle));
DestroyIcon(local_icon_handle);
}
}
if (bitmap_handles.size() > newplus_icon_index && bitmap_handles[newplus_icon_index])
{
newplus_main_context_menu_item.fMask |= MIIM_BITMAP;
newplus_main_context_menu_item.hbmpItem = bitmap_handles[newplus_icon_index];
}
menu_id++;
// Add template items to context menu
int index = 0;
for (; index < number_of_templates; index++)
{
const auto template_item = templates->get_template_item(index);
add_template_item_to_context_menu(sub_menu_of_templates, sub_menu_index, template_item, menu_id, index);
menu_id++;
sub_menu_index++;
}
// Add separator to context menu
add_separator_to_context_menu(sub_menu_of_templates, sub_menu_index);
sub_menu_index++;
// Add "Open templates" item to context menu
add_open_templates_to_context_menu(sub_menu_of_templates, sub_menu_index, template_folder_root, menu_id, index);
menu_id++;
if (!InsertMenuItem(menu_handle, menu_index, TRUE, &newplus_main_context_menu_item))
{
Logger::error(L"QueryContextMenu() failed. {}", get_last_error_or_default(GetLastError()));
return HRESULT_FROM_WIN32(GetLastError());
}
else
{
// Return the amount if entries inserted
const auto number_of_items_inserted = menu_id - menu_first_cmd_id;
return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, number_of_items_inserted);
}
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
}
return E_FAIL;
}
void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index)
{
static const std::wstring localized_context_menu_item_open_templates =
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates");
wchar_t menu_name_open[256] = { 0 };
wcscpy_s(menu_name_open, ARRAYSIZE(menu_name_open), localized_context_menu_item_open_templates.c_str());
const auto open_folder_item = Make<template_folder_context_menu_item>(template_folder_root);
MENUITEMINFO newplus_menu_item_open_templates;
newplus_menu_item_open_templates.cbSize = sizeof(MENUITEMINFO);
newplus_menu_item_open_templates.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID;
newplus_menu_item_open_templates.wID = menu_id;
newplus_menu_item_open_templates.fType = MFT_STRING;
newplus_menu_item_open_templates.dwTypeData = (PWSTR)menu_name_open;
const auto open_templates_icon_index = index + 1;
if (bitmap_handles.size() <= open_templates_icon_index)
{
const std::wstring icon_file = utilities::get_open_templates_icon_resource_filepath(
module_instance_handle, ThemeHelpers::GetAppTheme())
.c_str();
HICON open_template_icon_handle = static_cast<HICON>(
LoadImage(NULL, icon_file.c_str(), IMAGE_ICON, GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON), LR_LOADFROMFILE));
if (open_template_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(open_template_icon_handle));
DestroyIcon(open_template_icon_handle);
}
}
if (bitmap_handles.size() > open_templates_icon_index && bitmap_handles[open_templates_icon_index])
{
newplus_menu_item_open_templates.fMask |= MIIM_BITMAP;
newplus_menu_item_open_templates.hbmpItem = bitmap_handles[open_templates_icon_index];
}
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_open_templates);
}
void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index)
{
MENUITEMINFO menu_item_separator;
menu_item_separator.cbSize = sizeof(MENUITEMINFO);
menu_item_separator.fMask = MIIM_FTYPE;
menu_item_separator.fType = MFT_SEPARATOR;
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &menu_item_separator);
}
void shell_context_menu_win10::add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index)
{
wchar_t menu_name[256] = { 0 };
wcscpy_s(menu_name, ARRAYSIZE(menu_name), template_item->get_menu_title(!utilities::get_newplus_setting_hide_extension(), !utilities::get_newplus_setting_hide_starting_digits()).c_str());
MENUITEMINFO newplus_menu_item_template;
newplus_menu_item_template.cbSize = sizeof(MENUITEMINFO);
newplus_menu_item_template.fMask = MIIM_STRING | MIIM_FTYPE | MIIM_ID | MIIM_DATA;
newplus_menu_item_template.wID = menu_id;
newplus_menu_item_template.fType = MFT_STRING;
newplus_menu_item_template.dwTypeData = (PWSTR)menu_name;
const auto current_template_icon_index = index + 1;
if (bitmap_handles.size() <= current_template_icon_index)
{
HICON template_icon_handle = template_item->get_explorer_icon_handle();
if (template_icon_handle)
{
bitmap_handles.push_back(CreateBitmapFromIcon(template_icon_handle));
DestroyIcon(template_icon_handle);
}
}
if (bitmap_handles.size() > current_template_icon_index && bitmap_handles[current_template_icon_index])
{
newplus_menu_item_template.fMask |= MIIM_BITMAP;
newplus_menu_item_template.hbmpItem = bitmap_handles[current_template_icon_index];
}
InsertMenuItem(sub_menu_of_templates, sub_menu_index, TRUE, &newplus_menu_item_template);
}
IFACEMETHODIMP shell_context_menu_win10::InvokeCommand(CMINVOKECOMMANDINFO* params)
{
if (!params)
{
return E_FAIL;
}
// Get selected menu item (a template or the "Open templates" item)
const auto selected_menu_item_index = LOWORD(params->lpVerb) - 1;
if (selected_menu_item_index < 0)
{
return E_FAIL;
}
const auto number_of_templates = templates->list_of_templates.size();
const bool is_template_item = selected_menu_item_index < number_of_templates;
// Save how many item templates we have so it can be sent later when we do something with New+.
// It will be sent when the user does something, similar to Windows 11 context menu.
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
if (is_template_item)
{
// It's a template menu item
const auto template_entry = templates->get_template_item(selected_menu_item_index);
return newplus::utilities::copy_template(template_entry, site_of_folder);
}
else
{
// It's the "Open templates" menu item
const std::filesystem::path template_folder_root = utilities::get_new_template_folder_location();
return newplus::utilities::open_template_folder(template_folder_root);
}
return E_FAIL;
}
IFACEMETHODIMP shell_context_menu_win10::GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT)
{
return E_NOTIMPL;
}
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP shell_context_menu_win10::SetSite(_In_ IUnknown* site) noexcept
{
this->site_of_folder = site;
return S_OK;
}
IFACEMETHODIMP shell_context_menu_win10::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept
{
return this->site_of_folder.CopyTo(riid, returned_site);
}
#pragma endregion

View File

@@ -0,0 +1,45 @@
#pragma once
#include "pch.h"
#include <template_folder.h>
using namespace Microsoft::WRL;
#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID "FF90D477-E32A-4BE8-8CC5-A502A97F5401"
// File Explorer context menu "New+" for Windows 10
class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_WIN10_UUID)) shell_context_menu_win10 :
public RuntimeClass<
RuntimeClassFlags<ClassicCom>,
IShellExtInit,
IContextMenu,
IObjectWithSite>
{
public:
~shell_context_menu_win10();
#pragma region IShellExtInit
IFACEMETHODIMP Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject*, HKEY);
#pragma endregion
#pragma region IContextMenu
IFACEMETHODIMP QueryContextMenu(HMENU menu_handle, UINT menu_index, UINT menu_first_cmd_id, UINT, UINT menu_flags);
IFACEMETHODIMP InvokeCommand(CMINVOKECOMMANDINFO* pici);
IFACEMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, CHAR*, UINT);
#pragma endregion
#pragma region IObjectWithSite
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
#pragma endregion
protected:
void add_open_templates_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, const std::filesystem::path& template_folder_root, int menu_id, int index);
void add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index);
void add_template_item_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index, newplus::template_item* const template_item, int menu_id, int index);
HINSTANCE instance_handle = 0;
ComPtr<IUnknown> site_of_folder;
newplus::template_folder* templates = nullptr;
std::vector<HBITMAP> bitmap_handles;
};

View File

@@ -128,6 +128,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
<ClInclude Include="template_item.h" /> <ClInclude Include="template_item.h" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="new_utilities.cpp" />
<ClCompile Include="shell_context_menu.cpp" /> <ClCompile Include="shell_context_menu.cpp" />
<ClCompile Include="shell_context_sub_menu.cpp" /> <ClCompile Include="shell_context_sub_menu.cpp" />
<ClCompile Include="shell_context_sub_menu_item.cpp" /> <ClCompile Include="shell_context_sub_menu_item.cpp" />

View File

@@ -31,6 +31,9 @@
<ClCompile Include="trace.cpp"> <ClCompile Include="trace.cpp">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="new_utilities.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="template_folder.h"> <ClInclude Include="template_folder.h">
@@ -123,9 +126,6 @@
<None Include="AppxManifest.xml"> <None Include="AppxManifest.xml">
<Filter>Source Files</Filter> <Filter>Source Files</Filter>
</None> </None>
<None Include="Assets\NewPlus\New.ico">
<Filter>Asset Files</Filter>
</None>
<None Include="Assets\NewPlus\SmallTile.png"> <None Include="Assets\NewPlus\SmallTile.png">
<Filter>Asset Files</Filter> <Filter>Asset Files</Filter>
</None> </None>
@@ -190,14 +190,8 @@
</ResourceCompile> </ResourceCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Text Include="TemplateExamples\Example folder\Another example txt file.txt"> <CopyFileToFolders Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt" />
<Filter>Template Examples\Example folder</Filter> <CopyFileToFolders Include="TemplateExamples\Example folder\Example txt file.txt" />
</Text> <CopyFileToFolders Include="TemplateExamples\Example folder\Another example txt file.txt" />
<Text Include="TemplateExamples\Example folder\Example txt file.txt">
<Filter>Template Examples\Example folder</Filter>
</Text>
<Text Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt">
<Filter>Template Examples</Filter>
</Text>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -0,0 +1,13 @@
#include "pch.h"
#include "new_utilities.h"
// HACK: Store number of templates when generating the menu entries to send later.
size_t saved_number_of_templates = -1;
size_t newplus::utilities::get_saved_number_of_templates()
{
return saved_number_of_templates;
}
void newplus::utilities::set_saved_number_of_templates(size_t templates)
{
saved_number_of_templates = templates;
}

View File

@@ -7,11 +7,17 @@
#include "constants.h" #include "constants.h"
#include "settings.h" #include "settings.h"
#include "template_item.h"
#include "trace.h"
#pragma comment(lib, "Shlwapi.lib") #pragma comment(lib, "Shlwapi.lib")
using namespace newplus;
namespace newplus::utilities namespace newplus::utilities
{ {
size_t get_saved_number_of_templates();
void set_saved_number_of_templates(size_t templates);
inline std::wstring get_explorer_icon(std::filesystem::path path) inline std::wstring get_explorer_icon(std::filesystem::path path)
{ {
@@ -39,6 +45,33 @@ namespace newplus::utilities
return icon_resource; return icon_resource;
} }
inline HICON get_explorer_icon_handle(std::filesystem::path path)
{
SHFILEINFO shell_file_info = { 0 };
const std::wstring filepath = path.wstring();
DWORD_PTR result = SHGetFileInfo(filepath.c_str(), 0, &shell_file_info, sizeof(shell_file_info), SHGFI_ICON);
if (shell_file_info.hIcon)
{
return shell_file_info.hIcon;
}
WCHAR icon_resource_specifier[MAX_PATH] = { 0 };
DWORD buffer_length = MAX_PATH;
const std::wstring extension = path.extension().wstring();
const HRESULT hr = AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN,
ASSOCSTR_DEFAULTICON,
extension.c_str(),
NULL,
icon_resource_specifier,
&buffer_length);
const std::wstring icon_resource = icon_resource_specifier;
const auto icon_x = GetSystemMetrics(SM_CXSMICON);
const auto icon_y = GetSystemMetrics(SM_CYSMICON);
HICON hIcon = static_cast<HICON>(LoadImage(NULL, icon_resource.c_str(), IMAGE_ICON, icon_x, icon_y, LR_LOADFROMFILE));
return hIcon;
}
inline bool is_hidden(const std::filesystem::path path) inline bool is_hidden(const std::filesystem::path path)
{ {
const std::filesystem::path::string_type name = path.filename(); const std::filesystem::path::string_type name = path.filename();
@@ -180,4 +213,227 @@ namespace newplus::utilities
return path; return path;
} }
inline bool is_desktop_folder(const std::filesystem::path target_fullpath)
{
TCHAR desktopPath[MAX_PATH];
if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, desktopPath)))
{
return StrCmpIW(target_fullpath.c_str(), desktopPath) == 0;
}
return false;
}
inline void explorer_enter_rename_mode(const std::filesystem::path target_fullpath_of_new_instance)
{
const std::filesystem::path path_without_new_file_or_dir = target_fullpath_of_new_instance.parent_path();
const std::filesystem::path new_file_or_dir_without_path = target_fullpath_of_new_instance.filename();
ComPtr<IShellWindows> shell_windows;
HRESULT hr;
if (FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_ALL, IID_PPV_ARGS(&shell_windows))))
{
return;
}
long window_handle;
ComPtr<IDispatch> shell_window;
const bool object_created_on_desktop = is_desktop_folder(path_without_new_file_or_dir.c_str());
if (object_created_on_desktop)
{
// Special handling for desktop folder
VARIANT empty_yet_needed_incl_init;
VariantInit(&empty_yet_needed_incl_init);
if (FAILED(shell_windows->FindWindowSW(&empty_yet_needed_incl_init, &empty_yet_needed_incl_init, SWC_DESKTOP, &window_handle, SWFO_NEEDDISPATCH, &shell_window)))
{
return;
}
}
else
{
long count_of_shell_windows = 0;
shell_windows->get_Count(&count_of_shell_windows);
for (long i = 0; i < count_of_shell_windows; ++i)
{
ComPtr<IWebBrowserApp> web_browser_app;
VARIANT v;
V_VT(&v) = VT_I4;
V_I4(&v) = i;
hr = shell_windows->Item(v, &shell_window);
if (SUCCEEDED(hr) && shell_window)
{
hr = shell_window.As(&web_browser_app);
if (SUCCEEDED(hr))
{
BSTR folder_view_location;
hr = web_browser_app->get_LocationURL(&folder_view_location);
if (SUCCEEDED(hr) && folder_view_location)
{
wchar_t path[MAX_PATH];
DWORD pathLength = ARRAYSIZE(path);
hr = PathCreateFromUrl(folder_view_location, path, &pathLength, 0);
SysFreeString(folder_view_location);
if (SUCCEEDED(hr) && StrCmpIW(path_without_new_file_or_dir.c_str(), path) == 0)
{
break;
}
}
}
}
shell_window = nullptr;
}
}
if (!shell_window)
{
return;
}
ComPtr<IServiceProvider> service_provider;
shell_window.As(&service_provider);
ComPtr<IShellBrowser> shell_browser;
service_provider->QueryService(SID_STopLevelBrowser, IID_PPV_ARGS(&shell_browser));
ComPtr<IShellView> shell_view;
shell_browser->QueryActiveShellView(&shell_view);
ComPtr<IFolderView> folder_view;
shell_view.As(&folder_view);
// Find the newly created object (file or folder)
// And put object into edit mode (SVSI_EDIT) and if desktop also reposition
int number_of_objects_in_view = 0;
bool done = false;
folder_view->ItemCount(SVGIO_ALLVIEW, &number_of_objects_in_view);
for (int i = 0; i < number_of_objects_in_view && !done; ++i)
{
std::wstring path_of_item(MAX_PATH, 0);
LPITEMIDLIST shell_item_ids;
folder_view->Item(i, &shell_item_ids);
SHGetPathFromIDList(shell_item_ids, &path_of_item[0]);
const std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
if (utilities::wstring_same_when_comparing_ignore_case(new_file_or_dir_without_path, current_filename))
{
const DWORD common_select_flags = SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED;
if (object_created_on_desktop)
{
// Newly created object is on the desktop -- reposition under mouse and enter rename mode
LPCITEMIDLIST shell_item_to_select_and_position[] = { shell_item_ids };
POINT mouse_position;
GetCursorPos(&mouse_position);
mouse_position.x -= GetSystemMetrics(SM_CXMENUSIZE);
mouse_position.x = max(mouse_position.x, 20);
mouse_position.y -= GetSystemMetrics(SM_CXMENUSIZE)/2;
mouse_position.y = max(mouse_position.y, 20);
POINT position[] = { mouse_position };
folder_view->SelectAndPositionItems(1, shell_item_to_select_and_position, position, common_select_flags | SVSI_POSITIONITEM);
}
else
{
// Enter rename mode
folder_view->SelectItem(i, common_select_flags);
}
done = true;
}
CoTaskMemFree(shell_item_ids);
}
}
inline HRESULT copy_template(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
{
HRESULT hr = S_OK;
try
{
Logger::info(L"Copying template");
if (newplus::utilities::get_saved_number_of_templates() >= 0)
{
// Log that context menu was shown and with how many items
trace.UpdateState(true);
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
trace.Flush();
trace.UpdateState(false);
}
// Determine target path of where context menu was displayed
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
// Determine initial filename
std::filesystem::path source_fullpath = template_entry->path;
std::filesystem::path target_fullpath = std::wstring(target_path_name);
// Only append name to target if source is not a directory
if (!utilities::is_directory(source_fullpath))
{
target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
}
// Copy file and determine final filename
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
trace.UpdateState(true);
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
trace.Flush();
trace.UpdateState(false);
// Refresh folder items
template_entry->refresh_target(target_final_fullpath);
// Enter rename mode
template_entry->enter_rename_mode(target_final_fullpath);
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
hr = S_FALSE;
}
trace.UpdateState(true);
Trace::EventCopyTemplateResult(hr);
trace.Flush();
trace.UpdateState(false);
return hr;
}
inline HRESULT open_template_folder(const std::filesystem::path template_folder)
{
HRESULT hr = S_OK;
try
{
Logger::info(L"Open templates folder");
if (newplus::utilities::get_saved_number_of_templates() >= 0)
{
// Log that context menu was shown and with how many items
trace.UpdateState(true);
Trace::EventShowTemplateItems(newplus::utilities::get_saved_number_of_templates());
trace.Flush();
trace.UpdateState(false);
}
const std::wstring verb_hardcoded_do_not_change = L"open";
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
trace.UpdateState(true);
Trace::EventOpenTemplates();
trace.Flush();
trace.UpdateState(false);
}
catch (const std::exception& ex)
{
Logger::error(ex.what());
hr = S_FALSE;
}
return hr;
}
} }

View File

@@ -27,7 +27,6 @@ public:
#pragma region IObjectWithSite #pragma region IObjectWithSite
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept; IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept; IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
#pragma endregion #pragma endregion

View File

@@ -1,13 +1,13 @@
#include "pch.h" #include "pch.h"
#include "shell_context_sub_menu.h" #include "shell_context_sub_menu.h"
#include "trace.h" #include "trace.h"
#include "new_utilities.h"
using namespace Microsoft::WRL; using namespace Microsoft::WRL;
// // Sub context menu command enumerator // // Sub context menu command enumerator
shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder) shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder)
{ {
trace.UpdateState(true);
this->site_of_folder = site_of_folder; this->site_of_folder = site_of_folder;
// Determine the New+ Template folder location // Determine the New+ Template folder location
@@ -36,8 +36,9 @@ shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_fo
current_command = explorer_menu_item_commands.cbegin(); current_command = explorer_menu_item_commands.cbegin();
// Log that context menu was shown and with how many items // Save how many item templates we have so it can be sent later when we do something with New+.
Trace::EventShowTemplateItems(number_of_templates); // We don't send it here or it would send an event every time we open a context menu.
newplus::utilities::set_saved_number_of_templates(static_cast<size_t>(number_of_templates));
} }
// IEnumExplorerCommand // IEnumExplorerCommand

View File

@@ -63,49 +63,7 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* s
IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept
{ {
HRESULT hr = S_OK; return newplus::utilities::copy_template(template_entry, site_of_folder);
try
{
trace.UpdateState(true);
// Determine target path of where context menu was displayed
const auto target_path_name = utilities::get_path_from_unknown_site(site_of_folder);
// Determine initial filename
std::filesystem::path source_fullpath = template_entry->path;
std::filesystem::path target_fullpath = std::wstring(target_path_name);
// Only append name to target if source is not a directory
if (!utilities::is_directory(target_fullpath))
{
target_fullpath.append(this->template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
}
// Copy file and determine final filename
std::filesystem::path target_final_fullpath = this->template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
Trace::EventCopyTemplate(target_final_fullpath.extension().c_str());
// Refresh folder items
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
// Enter rename mode
this->template_entry->enter_rename_mode(site_of_folder, target_final_fullpath);
Trace::EventCopyTemplateResult(S_OK);
}
catch (const std::exception& ex)
{
Trace::EventCopyTemplateResult(S_FALSE);
Logger::error(ex.what());
hr = S_FALSE;
}
trace.Flush();
trace.UpdateState(false);
return hr;
} }
IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags) IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
@@ -162,9 +120,5 @@ IFACEMETHODIMP template_folder_context_menu_item::GetIcon(_In_opt_ IShellItemArr
IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
{ {
Logger::info(L"Open templates folder"); return newplus::utilities::open_template_folder(shell_template_folder);
const std::wstring verb_hardcoded_do_not_change = L"open";
ShellExecute(nullptr, verb_hardcoded_do_not_change.c_str(), shell_template_folder.c_str(), NULL, NULL, SW_SHOWNORMAL);
return S_OK;
} }

View File

@@ -11,6 +11,11 @@ template_folder::template_folder(const std::filesystem::path newplus_template_fo
this->template_folder_path = newplus_template_folder; this->template_folder_path = newplus_template_folder;
} }
template_folder::~template_folder()
{
list_of_templates.clear();
}
void template_folder::init() void template_folder::init()
{ {
rescan_template_folder(); rescan_template_folder();

View File

@@ -13,6 +13,8 @@ namespace newplus
{ {
public: public:
template_folder(const std::filesystem::path newplus_template_folder); template_folder(const std::filesystem::path newplus_template_folder);
~template_folder();
void rescan_template_folder(); void rescan_template_folder();
std::filesystem::path template_folder_path; std::filesystem::path template_folder_path;

View File

@@ -63,6 +63,11 @@ std::wstring template_item::get_explorer_icon() const
return utilities::get_explorer_icon(path); return utilities::get_explorer_icon(path);
} }
HICON template_item::get_explorer_icon_handle() const
{
return utilities::get_explorer_icon_handle(path);
}
std::filesystem::path template_item::copy_object_to(const HWND window_handle, const std::filesystem::path destination) const std::filesystem::path template_item::copy_object_to(const HWND window_handle, const std::filesystem::path destination) const
{ {
// SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs, // SHFILEOPSTRUCT wants the from and to paths to be terminated with two NULLs,
@@ -86,6 +91,14 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
if (!file_operation_params.hNameMappings) if (!file_operation_params.hNameMappings)
{ {
// No file name collision on copy // No file name collision on copy
if (utilities::is_directory(this->path))
{
// Append dir for consistency on directory naming inclusion for with and without collision
std::filesystem::path with_dir = destination;
with_dir /= this->path.filename();
return with_dir;
}
return destination; return destination;
} }
@@ -104,44 +117,23 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
return final_path; return final_path;
} }
void template_item::enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath) const void template_item::refresh_target(const std::filesystem::path target_final_fullpath) const
{ {
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, site, target_fullpath); SHChangeNotify(SHCNE_CREATE, SHCNF_PATH | SHCNF_FLUSH, target_final_fullpath.wstring().c_str(), NULL);
}
void template_item::enter_rename_mode(const std::filesystem::path target_fullpath) const
{
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, target_fullpath);
thread_for_renaming_workaround.detach(); thread_for_renaming_workaround.detach();
} }
void template_item::rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath) void template_item::rename_on_other_thread_workaround(const std::filesystem::path target_fullpath)
{ {
// Have been unable to have Windows Explorer Shell enter rename mode from the main thread // Have been unable to have Windows Explorer Shell enter rename mode from the main thread
// Sleep for a bit to only enter rename mode when icon has been drawn. Not strictly needed. // Sleep for a bit to only enter rename mode when icon has been drawn.
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 350 }; const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 50 };
std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed)); std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed));
const std::wstring filename = target_fullpath.filename(); newplus::utilities::explorer_enter_rename_mode(target_fullpath);
ComPtr<IServiceProvider> service_provider;
site->QueryInterface(IID_PPV_ARGS(&service_provider));
ComPtr<IFolderView> folder_view;
service_provider->QueryService(__uuidof(IFolderView), IID_PPV_ARGS(&folder_view));
int count = 0;
folder_view->ItemCount(SVGIO_ALLVIEW, &count);
for (int i = 0; i < count; ++i)
{
std::wstring path_of_item(MAX_PATH, 0);
LPITEMIDLIST pidl;
folder_view->Item(i, &pidl);
SHGetPathFromIDList(pidl, &path_of_item[0]);
CoTaskMemFree(pidl);
std::wstring current_filename = std::filesystem::path(path_of_item.c_str()).filename();
if (utilities::wstring_same_when_comparing_ignore_case(filename, current_filename))
{
folder_view->SelectItem(i, SVSI_EDIT | SVSI_SELECT | SVSI_DESELECTOTHERS | SVSI_ENSUREVISIBLE | SVSI_FOCUSED);
break;
}
}
} }

View File

@@ -21,14 +21,18 @@ namespace newplus
std::wstring get_explorer_icon() const; std::wstring get_explorer_icon() const;
HICON get_explorer_icon_handle() const;
std::filesystem::path copy_object_to(const HWND window_handle, const std::filesystem::path destination) const; std::filesystem::path copy_object_to(const HWND window_handle, const std::filesystem::path destination) const;
void enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_folder) const; void refresh_target(const std::filesystem::path target_final_fullpath) const;
void enter_rename_mode(const std::filesystem::path target_fullpath) const;
std::filesystem::path path; std::filesystem::path path;
private: private:
static void rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath); static void rename_on_other_thread_workaround(const std::filesystem::path target_fullpath);
std::wstring remove_starting_digits_from_filename(std::wstring filename) const; std::wstring remove_starting_digits_from_filename(std::wstring filename) const;
}; };

View File

@@ -58,3 +58,12 @@ void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept
TraceLoggingHResult(hr), TraceLoggingHResult(hr),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE)); TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
} }
void Trace::EventOpenTemplates() noexcept
{
TraceLoggingWriteWrapper(
g_hProvider,
"NewPlus_EventOpenTemplates",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
}

View File

@@ -12,4 +12,5 @@ public:
static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept; static void EventShowTemplateItems(_In_ const size_t number_of_templates) noexcept;
static void EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept; static void EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept;
static void EventCopyTemplateResult(_In_ const HRESULT hr) noexcept; static void EventCopyTemplateResult(_In_ const HRESULT hr) noexcept;
static void EventOpenTemplates() noexcept;
}; };

View File

@@ -23,13 +23,6 @@
<ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" /> <ToggleSwitch x:Uid="ToggleSwitch" IsOn="{x:Bind ViewModel.IsEnabled, Mode=TwoWay}" />
</tkcontrols:SettingsCard> </tkcontrols:SettingsCard>
<InfoBar
x:Uid="NewPlus_NoWindows10SupportWarning"
IsClosable="False"
IsOpen="{x:Bind ViewModel.IsWin10OrLower, Mode=OneWay}"
IsTabStop="{x:Bind ViewModel.IsWin10OrLower, Mode=OneWay}"
Severity="Warning" />
<InfoBar <InfoBar
x:Uid="GPO_SettingIsManaged" x:Uid="GPO_SettingIsManaged"
IsClosable="False" IsClosable="False"

View File

@@ -4498,9 +4498,6 @@ Activate by holding the key for the character you want to add an accent to, then
<value>Enable New+</value> <value>Enable New+</value>
<comment>Localize product name in accordance with Windows New</comment> <comment>Localize product name in accordance with Windows New</comment>
</data> </data>
<data name="NewPlus_NoWindows10SupportWarning.Title" xml:space="preserve">
<value>New+ is not supported in Windows 10 and is not expected to work.</value>
</data>
<data name="NewPlus_TemplatesNotBackupAndRestoreWarning.Title" xml:space="preserve"> <data name="NewPlus_TemplatesNotBackupAndRestoreWarning.Title" xml:space="preserve">
<value>PowerToys "Backup and Restore" feature doesn't take templates into account at this moment. If you use that feature, templates will have to be copied manually.</value> <value>PowerToys "Backup and Restore" feature doesn't take templates into account at this moment. If you use that feature, templates will have to be copied manually.</value>
</data> </data>