New Utility: New+ (#33136)
Co-authored-by: Jaime Bernardo <jaime@janeasystems.com> Co-authored-by: Stefan Markovic <stefan@janeasystems.com>
@@ -0,0 +1,56 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Package xmlns="http://schemas.microsoft.com/appx/manifest/foundation/windows10"
|
||||
xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10"
|
||||
xmlns:uap2="http://schemas.microsoft.com/appx/manifest/uap/windows10/2"
|
||||
xmlns:uap3="http://schemas.microsoft.com/appx/manifest/uap/windows10/3"
|
||||
xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"
|
||||
xmlns:desktop="http://schemas.microsoft.com/appx/manifest/desktop/windows10"
|
||||
xmlns:desktop4="http://schemas.microsoft.com/appx/manifest/desktop/windows10/4"
|
||||
xmlns:desktop5="http://schemas.microsoft.com/appx/manifest/desktop/windows10/5"
|
||||
xmlns:uap10="http://schemas.microsoft.com/appx/manifest/uap/windows10/10"
|
||||
xmlns:com="http://schemas.microsoft.com/appx/manifest/com/windows10" IgnorableNamespaces="uap uap2 uap3 rescap desktop desktop4 desktop5 uap10 com">
|
||||
<Identity Name="Microsoft.PowerToys.NewPlusContextMenu" ProcessorArchitecture="neutral" Publisher="CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US" Version="1.0.0.0" />
|
||||
<Properties>
|
||||
<DisplayName>PowerToys New+</DisplayName>
|
||||
<PublisherDisplayName>Microsoft</PublisherDisplayName>
|
||||
<Logo>Assets\NewPlus\StoreLogo.png</Logo>
|
||||
<uap10:AllowExternalContent>true</uap10:AllowExternalContent>
|
||||
</Properties>
|
||||
<Resources>
|
||||
<Resource Language="en-us" />
|
||||
</Resources>
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.18950.0" MaxVersionTested="10.0.19000.0" />
|
||||
</Dependencies>
|
||||
<Capabilities>
|
||||
<rescap:Capability Name="runFullTrust" />
|
||||
<rescap:Capability Name="unvirtualizedResources"/>
|
||||
</Capabilities>
|
||||
<Applications>
|
||||
<Application Id="Microsoft.PowerToys.NewPlusContextMenu" Executable="NewPlus.exe" uap10:TrustLevel="mediumIL" uap10:RuntimeBehavior="win32App">
|
||||
<uap:VisualElements AppListEntry="none" DisplayName="PowerToys New+" Description="New+ File Explorer Context Menu" BackgroundColor="transparent" Square150x150Logo="Assets\NewPlus\Square150x150Logo.png" Square44x44Logo="Assets\NewPlus\Square44x44Logo.png">
|
||||
<uap:DefaultTile Wide310x150Logo="Assets\NewPlus\Wide310x150Logo.png" Square310x310Logo="Assets\NewPlus\LargeTile.png" Square71x71Logo="Assets\NewPlus\SmallTile.png"></uap:DefaultTile>
|
||||
<uap:SplashScreen Image="Assets\NewPlus\SplashScreen.png" />
|
||||
</uap:VisualElements>
|
||||
<Extensions>
|
||||
<desktop4:Extension Category="windows.fileExplorerContextMenus">
|
||||
<desktop4:FileExplorerContextMenus>
|
||||
<desktop5:ItemType Type="Directory">
|
||||
<desktop5:Verb Id="NewPlusCommand" Clsid="69824FC6-4660-4A09-9E7C-48DA63C6CC0F" />
|
||||
</desktop5:ItemType>
|
||||
<desktop5:ItemType Type="Directory\Background">
|
||||
<desktop5:Verb Id="NewPlusCommand" Clsid="69824FC6-4660-4A09-9E7C-48DA63C6CC0F" />
|
||||
</desktop5:ItemType>
|
||||
</desktop4:FileExplorerContextMenus>
|
||||
</desktop4:Extension>
|
||||
<com:Extension Category="windows.comServer" uap10:RuntimeBehavior="packagedClassicApp">
|
||||
<com:ComServer>
|
||||
<com:SurrogateServer DisplayName="Context menu verb handler">
|
||||
<com:Class Id="69824FC6-4660-4A09-9E7C-48DA63C6CC0F" Path="PowerToys.NewPlus.ShellExtension.dll" ThreadingModel="STA"/>
|
||||
</com:SurrogateServer>
|
||||
</com:ComServer>
|
||||
</com:Extension>
|
||||
</Extensions>
|
||||
</Application>
|
||||
</Applications>
|
||||
</Package>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 264 KiB |
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
|
After Width: | Height: | Size: 433 B |
|
After Width: | Height: | Size: 328 B |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -0,0 +1,236 @@
|
||||
<?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>{8acb33d9-c95b-47d4-8363-9731ee0930a0}</ProjectGuid>
|
||||
<RootNamespace>NewPlusShellExtension</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.20348.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>NewPlus.ShellExtension</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v143</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</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 Condition="'$(Configuration)'=='Debug'">
|
||||
<TargetExt>.dll</TargetExt>
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
|
||||
<TargetName>PowerToys.NewPlus.ShellExtension</TargetName>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\</IntDir>
|
||||
<LinkIncremental />
|
||||
<IgnoreImportLibrary />
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)'=='Release'">
|
||||
<OutDir>..\..\..\..\$(Platform)\$(Configuration)\WinUI3Apps\</OutDir>
|
||||
<TargetName>PowerToys.NewPlus.ShellExtension</TargetName>
|
||||
<IntDir>$(SolutionDir)$(Platform)\$(Configuration)\TemporaryBuild\obj\$(ProjectName)\</IntDir>
|
||||
<LinkIncremental />
|
||||
<IgnoreImportLibrary />
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Debug'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;NEWPLUSCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>del $(OutDir)\NewPlusPackage.msix /q
|
||||
MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
</PreBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)'=='Release'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;NEWPLUSCONTEXTMENU_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<AdditionalIncludeDirectories>..\..\..\common\Telemetry;..\..\;..\..\..\;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<ModuleDefinitionFile>dll.def</ModuleDefinitionFile>
|
||||
<AdditionalDependencies>runtimeobject.lib;$(CoreLibraryDependencies)</AdditionalDependencies>
|
||||
<IgnoreSpecificDefaultLibraries>
|
||||
</IgnoreSpecificDefaultLibraries>
|
||||
</Link>
|
||||
<PreBuildEvent>
|
||||
<Command>del $(OutDir)\NewPlusPackage.msix /q
|
||||
MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
</PreBuildEvent>
|
||||
<PostBuildEvent>
|
||||
<Command>
|
||||
</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="dll_main.h" />
|
||||
<ClInclude Include="shell_context_menu.h" />
|
||||
<ClInclude Include="shell_context_sub_menu.h" />
|
||||
<ClInclude Include="shell_context_sub_menu_item.h" />
|
||||
<ClInclude Include="constants.h" />
|
||||
<ClInclude Include="settings.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="new_utilities.h" />
|
||||
<ClInclude Include="resource.base.h" />
|
||||
<ClInclude Include="template_folder.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
<ClInclude Include="Generated Files/resource.h" />
|
||||
<ClInclude Include="template_item.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="shell_context_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu_item.cpp" />
|
||||
<ClCompile Include="dll_main.cpp" />
|
||||
<ClCompile Include="powertoys_module.cpp" />
|
||||
<ClCompile Include="settings.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="template_folder.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
<ClCompile Include="template_item.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="dll.def" />
|
||||
<None Include="packages.config" />
|
||||
<CopyFileToFolders Include="TemplateExamples\Any files or folders placed in the template folder are available via New+.txt">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<FileType>Document</FileType>
|
||||
<DestinationFolders>$(OutDir)\Assets\NewPlus\Templates</DestinationFolders>
|
||||
</CopyFileToFolders>
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Example txt file.txt">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<FileType>Document</FileType>
|
||||
<DestinationFolders>$(OutDir)\Assets\NewPlus\Templates\Example folder</DestinationFolders>
|
||||
</CopyFileToFolders>
|
||||
<CopyFileToFolders Include="TemplateExamples\Example folder\Another example txt file.txt">
|
||||
<DeploymentContent>true</DeploymentContent>
|
||||
<FileType>Document</FileType>
|
||||
<DestinationFolders>$(OutDir)\Assets\NewPlus\Templates\Example folder</DestinationFolders>
|
||||
</CopyFileToFolders>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\common\logger\logger.vcxproj">
|
||||
<Project>{d9b8fc84-322a-4f9f-bbb9-20915c47ddfd}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\SettingsAPI\SettingsAPI.vcxproj">
|
||||
<Project>{6955446d-23f7-4023-9bb3-8657f904af99}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\Themes\Themes.vcxproj">
|
||||
<Project>{98537082-0fdb-40de-abd8-0dc5a4269bab}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\..\..\common\version\version.vcxproj">
|
||||
<Project>{cc6e41ac-8174-4e8a-8d22-85dd7f4851df}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files/new.rc" />
|
||||
<None Include="new.base.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Assets\NewPlus\LargeTile.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New_dark.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New_light.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Open_templates_dark.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Open_templates_light.ico">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SplashScreen.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square150x150Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square44x44Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\StoreLogo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Wide310x150Logo.png">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="resources.resx">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="AppxManifest.xml" />
|
||||
</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')" />
|
||||
</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'))" />
|
||||
</Target>
|
||||
</Project>
|
||||
@@ -0,0 +1,203 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="shell_context_menu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_sub_menu.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="shell_context_sub_menu_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="template_folder.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="template_item.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dll_main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="powertoys_module.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="settings.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="template_folder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="template_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shell_context_sub_menu_item.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shell_context_sub_menu.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="shell_context_menu.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dll_main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="constants.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="new_utilities.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Generated Files/resource.h">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SplashScreen.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square150x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square44x44Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\StoreLogo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Wide310x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SplashScreen.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square150x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square44x44Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\StoreLogo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Wide310x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="resources.resx">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="new.base.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</None>
|
||||
<None Include="dll.def">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="AppxManifest.xml">
|
||||
<Filter>Source Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SmallTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\SplashScreen.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square150x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Square44x44Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\StoreLogo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Wide310x150Logo.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\LargeTile.png">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New_dark.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\New_light.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Open_templates_dark.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
<None Include="Assets\NewPlus\Open_templates_light.ico">
|
||||
<Filter>Asset Files</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{82bf7d46-1201-4fc2-99e0-6c1f48f97f1f}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{0c915b9c-0e6a-4d16-8620-507a10fc29c9}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Asset Files">
|
||||
<UniqueIdentifier>{0c64a1a0-1f9e-4663-8649-454da469d15c}</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="Resource Files">
|
||||
<UniqueIdentifier>{4f319851-7d86-4180-b3a3-fd497a320c34}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Generated Files">
|
||||
<UniqueIdentifier>{a998f674-d126-488b-8457-7673fe986f94}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Template Examples">
|
||||
<UniqueIdentifier>{b442cb0f-9f62-46e8-b269-fefa8ceacb21}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Template Examples\Example folder">
|
||||
<UniqueIdentifier>{e7904759-7b6c-4609-9c6d-e3eae3cb866c}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files/new.rc">
|
||||
<Filter>Generated Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Text Include="TemplateExamples\Example folder\Another example txt file.txt">
|
||||
<Filter>Template Examples\Example folder</Filter>
|
||||
</Text>
|
||||
<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>
|
||||
</Project>
|
||||
@@ -0,0 +1 @@
|
||||
Learn more about New+ by visiting https://aka.ms/PowerToysOverview_NewPlus
|
||||
@@ -0,0 +1 @@
|
||||
Another example txt file
|
||||
@@ -0,0 +1 @@
|
||||
Example txt file
|
||||
34
src/modules/NewPlus/NewShellExtensionContextMenu/constants.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
namespace newplus::constants::non_localizable
|
||||
{
|
||||
constexpr WCHAR powertoy_key[] = L"NewPlus";
|
||||
|
||||
constexpr WCHAR powertoy_name[] = L"NewPlus";
|
||||
|
||||
constexpr WCHAR settings_json_data_file_path[] = L"\\settings.json";
|
||||
|
||||
constexpr WCHAR settings_json_key_hide_file_extension[] = L"HideFileExtension";
|
||||
|
||||
constexpr WCHAR settings_json_key_hide_starting_digits[] = L"HideStartingDigits";
|
||||
|
||||
constexpr WCHAR settings_json_key_template_location[] = L"TemplateLocation";
|
||||
|
||||
constexpr WCHAR context_menu_package_name[] = L"NewPlusContextMenu";
|
||||
|
||||
constexpr WCHAR msix_package_name[] = L"NewPlusPackage.msix";
|
||||
|
||||
constexpr WCHAR module_name[] = L"NewPlus.ShellExtension";
|
||||
|
||||
constexpr WCHAR new_icon_light_resource_relative_path[] = L"\\Assets\\NewPlus\\New_light.ico";
|
||||
|
||||
constexpr WCHAR new_icon_dark_resource_relative_path[] = L"\\Assets\\NewPlus\\New_dark.ico";
|
||||
|
||||
constexpr WCHAR open_templates_icon_light_resource_relative_path[] = L"\\Assets\\NewPlus\\Open_templates_light.ico";
|
||||
|
||||
constexpr WCHAR open_templates_icon_dark_resource_relative_path[] = L"\\Assets\\NewPlus\\Open_templates_dark.ico";
|
||||
|
||||
constexpr WCHAR desktop_ini_filename[] = L"desktop.ini";
|
||||
}
|
||||
5
src/modules/NewPlus/NewShellExtensionContextMenu/dll.def
Normal file
@@ -0,0 +1,5 @@
|
||||
LIBRARY
|
||||
EXPORTS
|
||||
DllCanUnloadNow PRIVATE
|
||||
DllGetClassObject PRIVATE
|
||||
DllGetActivationFactory PRIVATE
|
||||
@@ -0,0 +1,41 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu.h"
|
||||
#include "dll_main.h"
|
||||
#include "trace.h"
|
||||
|
||||
HMODULE module_instance_handle = 0;
|
||||
|
||||
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 rclsid, _In_ REFIID riid, _Outptr_ LPVOID FAR* ppv)
|
||||
{
|
||||
return Module<InProc>::GetModule().GetClassObject(rclsid, riid, ppv);
|
||||
}
|
||||
|
||||
CoCreatableClass(shell_context_menu)
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
extern HMODULE module_instance_handle;
|
||||
49
src/modules/NewPlus/NewShellExtensionContextMenu/new.base.rc
Normal file
@@ -0,0 +1,49 @@
|
||||
#include <windows.h>
|
||||
#include "Generated Files/resource.h"
|
||||
#include "../../../common/version/version.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
#include "winres.h"
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
1 VERSIONINFO
|
||||
FILEVERSION FILE_VERSION
|
||||
PRODUCTVERSION PRODUCT_VERSION
|
||||
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS VS_FF_DEBUG
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS VOS_NT_WINDOWS32
|
||||
FILETYPE VFT_DLL
|
||||
FILESUBTYPE VFT2_UNKNOWN
|
||||
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.
|
||||
IDI_ICON1 ICON "Assets/NewPlus/New_light.ico"
|
||||
177
src/modules/NewPlus/NewShellExtensionContextMenu/new_utilities.h
Normal file
@@ -0,0 +1,177 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/utils/package.h>
|
||||
|
||||
#include "constants.h"
|
||||
#include "settings.h"
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
namespace newplus::utilities
|
||||
{
|
||||
|
||||
inline std::wstring get_explorer_icon(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_ICONLOCATION);
|
||||
std::wstring icon_path = shell_file_info.szDisplayName;
|
||||
if (icon_path != L"")
|
||||
{
|
||||
const int icon_index = shell_file_info.iIcon;
|
||||
std::wstring icon_resource = icon_path + std::wstring(L",") + std::to_wstring(icon_index);
|
||||
return icon_resource;
|
||||
}
|
||||
|
||||
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;
|
||||
return icon_resource;
|
||||
}
|
||||
|
||||
inline bool is_hidden(const std::filesystem::path path)
|
||||
{
|
||||
const std::filesystem::path::string_type name = path.filename();
|
||||
if (name == constants::non_localizable::desktop_ini_filename)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool wstring_same_when_comparing_ignore_case(std::wstring stringA, std::wstring stringB)
|
||||
{
|
||||
transform(stringA.begin(), stringA.end(), stringA.begin(), towupper);
|
||||
transform(stringB.begin(), stringB.end(), stringB.begin(), towupper);
|
||||
|
||||
return (stringA == stringB);
|
||||
}
|
||||
|
||||
inline void process_pending_window_messages(HWND window_handle = NULL)
|
||||
{
|
||||
if (window_handle == NULL)
|
||||
{
|
||||
window_handle = GetActiveWindow();
|
||||
}
|
||||
|
||||
MSG current_message;
|
||||
while (PeekMessage(¤t_message, window_handle, NULL, NULL, PM_REMOVE))
|
||||
{
|
||||
DispatchMessage(¤t_message);
|
||||
}
|
||||
}
|
||||
|
||||
inline std::wstring get_new_template_folder_location()
|
||||
{
|
||||
return NewSettingsInstance().GetTemplateLocation();
|
||||
}
|
||||
|
||||
inline bool get_newplus_setting_hide_extension()
|
||||
{
|
||||
return NewSettingsInstance().GetHideFileExtension();
|
||||
}
|
||||
|
||||
inline bool get_newplus_setting_hide_starting_digits()
|
||||
{
|
||||
return NewSettingsInstance().GetHideStartingDigits();
|
||||
}
|
||||
|
||||
inline void create_folder_if_not_exist(const std::filesystem::path path)
|
||||
{
|
||||
std::filesystem::create_directory(path);
|
||||
}
|
||||
|
||||
inline std::wstring get_new_icon_resource_filepath(const HMODULE module_instance_handle, const Theme theme)
|
||||
{
|
||||
auto iconResourcePath = get_module_folderpath(module_instance_handle);
|
||||
|
||||
if (theme == Theme::Dark)
|
||||
{
|
||||
iconResourcePath += constants::non_localizable::new_icon_dark_resource_relative_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defaulting to the Light icon
|
||||
iconResourcePath += constants::non_localizable::new_icon_light_resource_relative_path;
|
||||
}
|
||||
|
||||
return iconResourcePath;
|
||||
}
|
||||
|
||||
inline std::wstring get_open_templates_icon_resource_filepath(const HMODULE module_instance_handle, const Theme theme)
|
||||
{
|
||||
auto iconResourcePath = get_module_folderpath(module_instance_handle);
|
||||
|
||||
if (theme == Theme::Dark)
|
||||
{
|
||||
iconResourcePath += constants::non_localizable::open_templates_icon_dark_resource_relative_path;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Defaulting to the Light icon
|
||||
iconResourcePath += constants::non_localizable::open_templates_icon_light_resource_relative_path;
|
||||
}
|
||||
|
||||
return iconResourcePath;
|
||||
}
|
||||
|
||||
inline void init_logger()
|
||||
{
|
||||
LoggerHelpers::init_logger(
|
||||
constants::non_localizable::powertoy_name,
|
||||
constants::non_localizable::module_name,
|
||||
LogSettings::newLoggerName);
|
||||
}
|
||||
|
||||
inline void register_msix_package()
|
||||
{
|
||||
if (package::IsWin11OrGreater())
|
||||
{
|
||||
static const auto new_dll_path = get_module_folderpath(module_instance_handle);
|
||||
auto new_package_uri = new_dll_path + L"\\" + constants::non_localizable::msix_package_name;
|
||||
|
||||
if (!package::IsPackageRegistered(constants::non_localizable::context_menu_package_name))
|
||||
{
|
||||
package::RegisterSparsePackage(new_dll_path, new_package_uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline std::wstring get_path_from_unknown_site(const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
ComPtr<IServiceProvider> service_provider;
|
||||
site_of_folder->QueryInterface(IID_PPV_ARGS(&service_provider));
|
||||
ComPtr<IFolderView> folder_view;
|
||||
service_provider->QueryService(__uuidof(IFolderView), IID_PPV_ARGS(&folder_view));
|
||||
ComPtr<IShellFolder> shell_folder;
|
||||
folder_view->GetFolder(IID_PPV_ARGS(&shell_folder));
|
||||
STRRET strings_returned;
|
||||
shell_folder->GetDisplayNameOf(0, SHGDN_FORPARSING, &strings_returned);
|
||||
LPWSTR path;
|
||||
StrRetToStr(&strings_returned, NULL, &path);
|
||||
return path;
|
||||
}
|
||||
|
||||
inline std::wstring get_path_from_folder_view(const ComPtr<IFolderView> folder_view)
|
||||
{
|
||||
ComPtr<IShellFolder> shell_folder;
|
||||
folder_view->GetFolder(IID_PPV_ARGS(&shell_folder));
|
||||
STRRET strings_returned;
|
||||
shell_folder->GetDisplayNameOf(0, SHGDN_FORPARSING, &strings_returned);
|
||||
LPWSTR path;
|
||||
StrRetToStr(&strings_returned, NULL, &path);
|
||||
return path;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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>
|
||||
3
src/modules/NewPlus/NewShellExtensionContextMenu/pch.cpp
Normal file
@@ -0,0 +1,3 @@
|
||||
// pch.cpp: source file corresponding to the pre-compiled header
|
||||
|
||||
#include "pch.h"
|
||||
37
src/modules/NewPlus/NewShellExtensionContextMenu/pch.h
Normal file
@@ -0,0 +1,37 @@
|
||||
// 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 <unknwn.h>
|
||||
|
||||
// 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"
|
||||
@@ -0,0 +1,134 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
#include <winrt/Windows.Data.Json.h>
|
||||
|
||||
#include <common/SettingsAPI/settings_objects.h>
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/process_path.h>
|
||||
#include <common/utils/resources.h>
|
||||
#include <interface/powertoy_module_interface.h>
|
||||
|
||||
#include "constants.h"
|
||||
#include "settings.h"
|
||||
#include "trace.h"
|
||||
#include "new_utilities.h"
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
// Note: Settings are managed via Settings and UI Settings
|
||||
class NewModule : public PowertoyModuleIface
|
||||
{
|
||||
public:
|
||||
NewModule()
|
||||
{
|
||||
init_settings();
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_name() override
|
||||
{
|
||||
static const std::wstring localized_context_menu_item =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
|
||||
|
||||
return localized_context_menu_item.c_str();
|
||||
}
|
||||
|
||||
virtual const wchar_t* get_key() override
|
||||
{
|
||||
// This setting key must match EnabledModules.cs [JsonPropertyName("NewPlus")]
|
||||
return newplus::constants::non_localizable::powertoy_key;
|
||||
}
|
||||
|
||||
virtual powertoys_gpo::gpo_rule_configured_t gpo_policy_enabled_configuration() override
|
||||
{
|
||||
return powertoys_gpo::getConfiguredNewPlusEnabledValue();
|
||||
}
|
||||
|
||||
virtual bool get_config(_Out_ PWSTR buffer, _Out_ int* buffer_size) override
|
||||
{
|
||||
// Not implemented as Settings are propagating via json
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void set_config(PCWSTR config) override
|
||||
{
|
||||
// The following just checks to see if the Template Location was changed for metrics purposes
|
||||
// Note: We are not saving the settings here and instead relying on read/write of json in Settings App .cs code paths
|
||||
try
|
||||
{
|
||||
json::JsonObject config_as_json = json::JsonValue::Parse(winrt::to_hstring(config)).GetObjectW();
|
||||
|
||||
const auto latest_location_value = config_as_json.GetNamedString(newplus::constants::non_localizable::settings_json_key_template_location).data();
|
||||
const auto existing_location_value = NewSettingsInstance().GetTemplateLocation();
|
||||
|
||||
if (!newplus::utilities::wstring_same_when_comparing_ignore_case(latest_location_value, existing_location_value))
|
||||
{
|
||||
Trace::EventChangedTemplateLocation();
|
||||
}
|
||||
}
|
||||
catch (std::exception& e)
|
||||
{
|
||||
Logger::error("Configuration parsing failed: {}", std::string{ e.what() });
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool is_enabled_by_default() const override
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual void enable() override
|
||||
{
|
||||
Logger::info("New+ enabled via Settings UI");
|
||||
|
||||
newplus::utilities::register_msix_package();
|
||||
|
||||
powertoy_new_enabled = true;
|
||||
}
|
||||
|
||||
virtual void disable() override
|
||||
{
|
||||
Logger::info("New+ disabled via Settings UI");
|
||||
|
||||
powertoy_new_enabled = false;
|
||||
}
|
||||
|
||||
virtual bool is_enabled() override
|
||||
{
|
||||
return powertoy_new_enabled;
|
||||
}
|
||||
|
||||
virtual void hide_file_extension(bool hide_file_extension)
|
||||
{
|
||||
Logger::info("New+ hide file extension {}", hide_file_extension);
|
||||
}
|
||||
|
||||
virtual void hide_starting_digits(bool hide_starting_digits)
|
||||
{
|
||||
Logger::info("New+ hide starting digits {}", hide_starting_digits);
|
||||
}
|
||||
|
||||
virtual void template_location(std::wstring path_location)
|
||||
{
|
||||
Logger::info("New+ template location");
|
||||
}
|
||||
|
||||
virtual void destroy() override
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool powertoy_new_enabled = false;
|
||||
|
||||
void init_settings()
|
||||
{
|
||||
powertoy_new_enabled = NewSettingsInstance().GetEnabled();
|
||||
}
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) PowertoyModuleIface* __cdecl powertoy_create()
|
||||
{
|
||||
return new NewModule();
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by AlwaysOnTop.rc
|
||||
|
||||
//////////////////////////////
|
||||
// Non-localizable
|
||||
|
||||
#define FILE_DESCRIPTION "PowerToys.New+"
|
||||
#define INTERNAL_NAME "PowerToys.New+"
|
||||
#define ORIGINAL_FILENAME "PowerToys.NewPlus.ShellExtension.dll"
|
||||
|
||||
// Non-localizable
|
||||
//////////////////////////////
|
||||
132
src/modules/NewPlus/NewShellExtensionContextMenu/resources.resx
Normal 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>
|
||||
225
src/modules/NewPlus/NewShellExtensionContextMenu/settings.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include <common/utils/gpo.h>
|
||||
#include <common/utils/json.h>
|
||||
#include <common/SettingsAPI/settings_helpers.h>
|
||||
|
||||
#include "settings.h"
|
||||
#include "constants.h"
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
// NewSettings are stored in PowerToys/New/settings.json
|
||||
// The New PowerToy enabled state is stored in the general PowerToys/settings.json
|
||||
|
||||
static bool LastModifiedTime(const std::wstring& file_Path, FILETIME* returned_file_timestamp)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr{};
|
||||
if (GetFileAttributesExW(file_Path.c_str(), GetFileExInfoStandard, &attr))
|
||||
{
|
||||
*returned_file_timestamp = attr.ftLastWriteTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
NewSettings::NewSettings()
|
||||
{
|
||||
// New+ overall enable state is stored in the general settings json file
|
||||
general_settings_json_file_path = PTSettingsHelper::get_powertoys_general_save_file_location();
|
||||
|
||||
// New+' actual settings are stored in new_settings_json_file_path
|
||||
std::wstring settings_save_path = PTSettingsHelper::get_module_save_folder_location(newplus::constants::non_localizable::powertoy_key);
|
||||
new_settings_json_file_path = settings_save_path + newplus::constants::non_localizable::settings_json_data_file_path;
|
||||
|
||||
RefreshEnabledState();
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
void NewSettings::Save()
|
||||
{
|
||||
json::JsonObject new_settings_json_data;
|
||||
|
||||
new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_hide_file_extension,
|
||||
json::value(new_settings.hide_file_extension));
|
||||
|
||||
new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_hide_starting_digits,
|
||||
json::value(new_settings.hide_starting_digits));
|
||||
|
||||
new_settings_json_data.SetNamedValue(newplus::constants::non_localizable::settings_json_key_template_location,
|
||||
json::value(new_settings.template_location));
|
||||
|
||||
json::to_file(new_settings_json_file_path, new_settings_json_data);
|
||||
|
||||
GetSystemTimeAsFileTime(&new_settings_last_loaded_timestamp);
|
||||
}
|
||||
|
||||
void NewSettings::Load()
|
||||
{
|
||||
if (!std::filesystem::exists(new_settings_json_file_path))
|
||||
{
|
||||
InitializeWithDefaultSettings();
|
||||
|
||||
Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseJson();
|
||||
}
|
||||
}
|
||||
|
||||
void NewSettings::InitializeWithDefaultSettings()
|
||||
{
|
||||
// Init the default New settings - in case the New/settings.json doesn't exist
|
||||
// Currently a similar defaulting logic is also in InitializeWithDefaultSettings in NewViewModel.cs
|
||||
SetHideFileExtension(true);
|
||||
|
||||
SetTemplateLocation(GetTemplateLocationDefaultPath());
|
||||
}
|
||||
|
||||
void NewSettings::RefreshEnabledState()
|
||||
{
|
||||
// Load json general settings from data file, if it was modified since we last checked
|
||||
FILETIME last_modified_timestamp{};
|
||||
if (!(LastModifiedTime(general_settings_json_file_path, &last_modified_timestamp) &&
|
||||
CompareFileTime(&last_modified_timestamp, &general_settings_last_loaded_timestamp) == 1))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
general_settings_last_loaded_timestamp = last_modified_timestamp;
|
||||
|
||||
auto json = json::from_file(general_settings_json_file_path);
|
||||
if (!json)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Load the enabled settings for the New PowerToy via the general settings
|
||||
const json::JsonObject& json_general_settings = json.value();
|
||||
try
|
||||
{
|
||||
json::JsonObject powertoy_new_enabled_state;
|
||||
json::get(json_general_settings, L"enabled", powertoy_new_enabled_state, json::JsonObject{});
|
||||
json::get(powertoy_new_enabled_state, newplus::constants::non_localizable::powertoy_key, new_settings.enabled, false);
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
Logger::error(L"New+ unable to load enabled state from json");
|
||||
}
|
||||
}
|
||||
|
||||
void NewSettings::Reload()
|
||||
{
|
||||
// Load json New settings from data file, if it was modified since we last checked.
|
||||
FILETIME very_latest_modified_timestamp{};
|
||||
if (LastModifiedTime(new_settings_json_file_path, &very_latest_modified_timestamp) &&
|
||||
CompareFileTime(&very_latest_modified_timestamp, &new_settings_last_loaded_timestamp) == 1)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
}
|
||||
|
||||
void NewSettings::ParseJson()
|
||||
{
|
||||
auto json = json::from_file(new_settings_json_file_path);
|
||||
if (json)
|
||||
{
|
||||
try
|
||||
{
|
||||
const json::JsonObject& new_settings_json = json.value();
|
||||
|
||||
if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_hide_file_extension, json::JsonValueType::Boolean))
|
||||
{
|
||||
new_settings.hide_file_extension = new_settings_json.GetNamedBoolean(
|
||||
newplus::constants::non_localizable::settings_json_key_hide_file_extension);
|
||||
}
|
||||
|
||||
if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_hide_starting_digits, json::JsonValueType::Boolean))
|
||||
{
|
||||
new_settings.hide_starting_digits = new_settings_json.GetNamedBoolean(
|
||||
newplus::constants::non_localizable::settings_json_key_hide_starting_digits);
|
||||
}
|
||||
|
||||
if (json::has(new_settings_json, newplus::constants::non_localizable::settings_json_key_template_location, json::JsonValueType::String))
|
||||
{
|
||||
new_settings.template_location = new_settings_json.GetNamedString(
|
||||
newplus::constants::non_localizable::settings_json_key_template_location);
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error&)
|
||||
{
|
||||
}
|
||||
}
|
||||
GetSystemTimeAsFileTime(&new_settings_last_loaded_timestamp);
|
||||
}
|
||||
|
||||
bool NewSettings::GetEnabled()
|
||||
{
|
||||
auto gpoSetting = powertoys_gpo::getConfiguredNewPlusEnabledValue();
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Reload();
|
||||
|
||||
RefreshEnabledState();
|
||||
|
||||
return new_settings.enabled;
|
||||
}
|
||||
|
||||
bool NewSettings::GetHideFileExtension() const
|
||||
{
|
||||
return new_settings.hide_file_extension;
|
||||
}
|
||||
|
||||
void NewSettings::SetHideFileExtension(const bool hide_file_extension)
|
||||
{
|
||||
new_settings.hide_file_extension = hide_file_extension;
|
||||
}
|
||||
|
||||
bool NewSettings::GetHideStartingDigits() const
|
||||
{
|
||||
return new_settings.hide_starting_digits;
|
||||
}
|
||||
|
||||
void NewSettings::SetHideStartingDigits(const bool hide_starting_digits)
|
||||
{
|
||||
new_settings.hide_starting_digits = hide_starting_digits;
|
||||
}
|
||||
|
||||
std::wstring NewSettings::GetTemplateLocation() const
|
||||
{
|
||||
return new_settings.template_location;
|
||||
}
|
||||
|
||||
void NewSettings::SetTemplateLocation(const std::wstring template_location)
|
||||
{
|
||||
new_settings.template_location = template_location;
|
||||
}
|
||||
|
||||
std::wstring NewSettings::GetTemplateLocationDefaultPath()
|
||||
{
|
||||
static const std::wstring default_template_sub_folder_name =
|
||||
GET_RESOURCE_STRING_FALLBACK(
|
||||
IDS_DEFAULT_TEMPLATE_SUB_FOLDER_NAME_WHERE_TEMPLATES_ARE_STORED,
|
||||
L"Templates");
|
||||
|
||||
static const std::wstring full_path = PTSettingsHelper::get_module_save_folder_location(
|
||||
newplus::constants::non_localizable::powertoy_key) +
|
||||
L"\\" + default_template_sub_folder_name;
|
||||
|
||||
return full_path;
|
||||
}
|
||||
|
||||
NewSettings& NewSettingsInstance()
|
||||
{
|
||||
static NewSettings instance;
|
||||
|
||||
return instance;
|
||||
}
|
||||
45
src/modules/NewPlus/NewShellExtensionContextMenu/settings.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
class NewSettings
|
||||
{
|
||||
public:
|
||||
NewSettings();
|
||||
|
||||
bool GetEnabled();
|
||||
bool GetHideFileExtension() const;
|
||||
void SetHideFileExtension(const bool hide_file_extension);
|
||||
bool GetHideStartingDigits() const;
|
||||
void SetHideStartingDigits(const bool hide_starting_digits);
|
||||
std::wstring GetTemplateLocation() const;
|
||||
void SetTemplateLocation(const std::wstring template_location);
|
||||
|
||||
void Save();
|
||||
void Load();
|
||||
|
||||
private:
|
||||
struct Settings
|
||||
{
|
||||
// These values are not used
|
||||
bool enabled{ false };
|
||||
bool hide_file_extension{ true };
|
||||
bool hide_starting_digits{ true };
|
||||
std::wstring template_location;
|
||||
};
|
||||
|
||||
void RefreshEnabledState();
|
||||
void InitializeWithDefaultSettings();
|
||||
std::wstring GetTemplateLocationDefaultPath();
|
||||
|
||||
void Reload();
|
||||
void ParseJson();
|
||||
|
||||
Settings new_settings;
|
||||
std::wstring general_settings_json_file_path;
|
||||
std::wstring new_settings_json_file_path;
|
||||
FILETIME general_settings_last_loaded_timestamp{};
|
||||
FILETIME new_settings_last_loaded_timestamp{};
|
||||
};
|
||||
|
||||
NewSettings& NewSettingsInstance();
|
||||
@@ -0,0 +1,87 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "shell_context_menu.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "shell_context_sub_menu_item.h"
|
||||
#include "template_folder.h"
|
||||
#include "new_utilities.h"
|
||||
#include "settings.h"
|
||||
#include "trace.h"
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
#pragma region IExplorerCommand
|
||||
IFACEMETHODIMP shell_context_menu::GetTitle(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_title)
|
||||
{
|
||||
static const std::wstring localized_context_menu_item =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_NEW, L"New+");
|
||||
|
||||
return SHStrDup(localized_context_menu_item.c_str(), returned_title);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_icon)
|
||||
{
|
||||
*returned_icon = nullptr;
|
||||
|
||||
static const auto icon_resource_filepath = utilities::get_new_icon_resource_filepath(module_instance_handle, ThemeHelpers::GetAppTheme());
|
||||
|
||||
return SHStrDup(icon_resource_filepath.c_str(), returned_icon);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_tool_tip)
|
||||
{
|
||||
*returned_tool_tip = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::GetCanonicalName(_Out_ GUID* returned_id)
|
||||
{
|
||||
*returned_id = __uuidof(this);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::GetState(_In_opt_ IShellItemArray*, _In_ BOOL, _Out_ EXPCMDSTATE* returned_state)
|
||||
{
|
||||
if (!NewSettingsInstance().GetEnabled())
|
||||
{
|
||||
*returned_state = ECS_HIDDEN;
|
||||
}
|
||||
else
|
||||
{
|
||||
*returned_state = ECS_ENABLED;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::GetFlags(_Out_ EXPCMDFLAGS* returned_menu_item_flags)
|
||||
{
|
||||
*returned_menu_item_flags = ECF_HASSUBCOMMANDS;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_menu::EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** returned_enum_commands)
|
||||
{
|
||||
auto e = Make<shell_context_sub_menu>(site_of_folder);
|
||||
return e->QueryInterface(IID_PPV_ARGS(returned_enum_commands));
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP shell_context_menu::SetSite(_In_ IUnknown* site) noexcept
|
||||
{
|
||||
this->site_of_folder = site;
|
||||
return S_OK;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_menu::GetSite(_In_ REFIID riid, _COM_Outptr_ void** returned_site) noexcept
|
||||
{
|
||||
return this->site_of_folder.CopyTo(riid, returned_site);
|
||||
}
|
||||
#pragma endregion
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
#define NEW_SHELL_EXTENSION_EXPLORER_COMMAND_UUID_STR "69824FC6-4660-4A09-9E7C-48DA63C6CC0F"
|
||||
|
||||
// File Explorer context menu "New+"
|
||||
class __declspec(uuid(NEW_SHELL_EXTENSION_EXPLORER_COMMAND_UUID_STR)) shell_context_menu final :
|
||||
public RuntimeClass<
|
||||
RuntimeClassFlags<ClassicCom>,
|
||||
IExplorerCommand,
|
||||
IObjectWithSite>
|
||||
{
|
||||
public:
|
||||
#pragma region IExplorerCommand
|
||||
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_title);
|
||||
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_icon);
|
||||
IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* returned_tool_tip);
|
||||
IFACEMETHODIMP GetCanonicalName(_Out_ GUID* returned_id);
|
||||
IFACEMETHODIMP GetState(_In_opt_ IShellItemArray*, _In_ BOOL, _Out_ EXPCMDSTATE* returned_state);
|
||||
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept;
|
||||
IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* returned_menu_item_flags);
|
||||
IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** returned_enum_commands);
|
||||
#pragma endregion
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
HINSTANCE instance_handle = 0;
|
||||
ComPtr<IUnknown> site_of_folder;
|
||||
};
|
||||
@@ -0,0 +1,80 @@
|
||||
#include "pch.h"
|
||||
#include "shell_context_sub_menu.h"
|
||||
#include "trace.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// // Sub context menu command enumerator
|
||||
shell_context_sub_menu::shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
this->site_of_folder = site_of_folder;
|
||||
|
||||
// Determine the New+ Template folder location
|
||||
const std::filesystem::path 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(root);
|
||||
|
||||
// Scan the folder for any files and folders (the templates)
|
||||
templates = new template_folder(root);
|
||||
templates->rescan_template_folder();
|
||||
|
||||
// Add template items to context menu
|
||||
const auto number_of_templates = templates->list_of_templates.size();
|
||||
int index = 0;
|
||||
for (int i = 0; i < number_of_templates; i++)
|
||||
{
|
||||
explorer_menu_item_commands.push_back(Make<shell_context_sub_menu_item>(templates->get_template_item(i), site_of_folder));
|
||||
}
|
||||
|
||||
// Add separator to context menu
|
||||
explorer_menu_item_commands.push_back(Make<separator_context_menu_item>());
|
||||
|
||||
// Add "Open templates" item to context menu
|
||||
explorer_menu_item_commands.push_back(Make<template_folder_context_menu_item>(root));
|
||||
|
||||
current_command = explorer_menu_item_commands.cbegin();
|
||||
|
||||
// Log that context menu was shown and with how many items
|
||||
Trace::EventShowTemplateItems(number_of_templates);
|
||||
}
|
||||
|
||||
// IEnumExplorerCommand
|
||||
IFACEMETHODIMP shell_context_sub_menu::Next(ULONG celt, __out_ecount_part(celt, *pceltFetched) IExplorerCommand** apUICommand, __out_opt ULONG* pceltFetched)
|
||||
{
|
||||
ULONG fetched{ 0 };
|
||||
|
||||
if (pceltFetched)
|
||||
{
|
||||
*pceltFetched = 0ul;
|
||||
}
|
||||
|
||||
for (ULONG i = 0; (i < celt) && (current_command != explorer_menu_item_commands.cend()); i++)
|
||||
{
|
||||
current_command->CopyTo(&apUICommand[0]);
|
||||
current_command++;
|
||||
fetched++;
|
||||
}
|
||||
|
||||
if (pceltFetched)
|
||||
{
|
||||
*pceltFetched = fetched;
|
||||
}
|
||||
|
||||
return (fetched == celt) ? S_OK : S_FALSE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu::Skip(ULONG)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_sub_menu::Reset()
|
||||
{
|
||||
current_command = explorer_menu_item_commands.cbegin();
|
||||
return S_OK;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_sub_menu::Clone(__deref_out IEnumExplorerCommand** ppenum)
|
||||
{
|
||||
*ppenum = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
#include "template_folder.h"
|
||||
#include "new_utilities.h"
|
||||
#include "shell_context_sub_menu_item.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
// // Sub context menu command enumerator
|
||||
class shell_context_sub_menu final : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IEnumExplorerCommand>
|
||||
{
|
||||
public:
|
||||
shell_context_sub_menu(const ComPtr<IUnknown> site_of_folder);
|
||||
|
||||
// IEnumExplorerCommand
|
||||
IFACEMETHODIMP Next(ULONG celt, __out_ecount_part(celt, *pceltFetched) IExplorerCommand** apUICommand, __out_opt ULONG* pceltFetched);
|
||||
IFACEMETHODIMP Skip(ULONG);
|
||||
IFACEMETHODIMP Reset();
|
||||
IFACEMETHODIMP Clone(__deref_out IEnumExplorerCommand** ppenum);
|
||||
|
||||
protected:
|
||||
std::vector<ComPtr<IExplorerCommand>> explorer_menu_item_commands;
|
||||
std::vector<ComPtr<IExplorerCommand>>::const_iterator current_command;
|
||||
template_folder* templates;
|
||||
ComPtr<IUnknown> site_of_folder;
|
||||
};
|
||||
@@ -0,0 +1,160 @@
|
||||
#include "pch.h"
|
||||
#include "shell_context_sub_menu_item.h"
|
||||
|
||||
#include "trace.h"
|
||||
#include "Generated Files/resource.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
// Sub context menu containing the actual list of templates
|
||||
shell_context_sub_menu_item::shell_context_sub_menu_item()
|
||||
{
|
||||
this->template_entry = nullptr;
|
||||
}
|
||||
|
||||
shell_context_sub_menu_item::shell_context_sub_menu_item(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder)
|
||||
{
|
||||
this->template_entry = template_entry;
|
||||
this->site_of_folder = site_of_folder;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* title)
|
||||
{
|
||||
return SHStrDup(this->template_entry->get_menu_title(
|
||||
!utilities::get_newplus_setting_hide_extension(),
|
||||
!utilities::get_newplus_setting_hide_starting_digits()
|
||||
).c_str(), title);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
|
||||
{
|
||||
return SHStrDup(this->template_entry->get_explorer_icon().c_str(), icon);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip)
|
||||
{
|
||||
*infoTip = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetCanonicalName(_Out_ GUID* guidCommandName)
|
||||
{
|
||||
*guidCommandName = GUID_NULL;
|
||||
return S_OK;
|
||||
}
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL, _Out_ EXPCMDSTATE* returned_state)
|
||||
{
|
||||
// Commented out for performance reasons
|
||||
|
||||
//DWORD object_count = 0;
|
||||
//selection->GetCount(&object_count);
|
||||
|
||||
//if (object_count == 1)
|
||||
//{
|
||||
// *returned_state = ECS_ENABLED;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// *returned_state = ECS_HIDDEN;
|
||||
//}
|
||||
|
||||
*returned_state = ECS_ENABLED;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::Invoke(_In_opt_ IShellItemArray*, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
try
|
||||
{
|
||||
// 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)
|
||||
+ L"\\"
|
||||
+ 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);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Trace::EventCopyTemplateResult(S_FALSE);
|
||||
Logger::error(ex.what());
|
||||
}
|
||||
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
|
||||
{
|
||||
*returned_flags = ECF_DEFAULT;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP shell_context_sub_menu_item::EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands)
|
||||
{
|
||||
*enumCommands = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
// Sub context menu - separator
|
||||
IFACEMETHODIMP separator_context_menu_item::GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* title)
|
||||
{
|
||||
title = nullptr;
|
||||
|
||||
// NOTE: Must by S_FALSE for the separator to show up
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP separator_context_menu_item::GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
|
||||
{
|
||||
*icon = nullptr;
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP separator_context_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
|
||||
{
|
||||
*returned_flags = ECF_ISSEPARATOR;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Sub context menu - "Open templates" New+ folder
|
||||
template_folder_context_menu_item::template_folder_context_menu_item(const std::filesystem::path shell_template_folder)
|
||||
{
|
||||
this->shell_template_folder = shell_template_folder;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP template_folder_context_menu_item::GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name)
|
||||
{
|
||||
static const std::wstring localized_context_menu_item =
|
||||
GET_RESOURCE_STRING_FALLBACK(IDS_CONTEXT_MENU_ITEM_OPEN_TEMPLATES, L"Open templates");
|
||||
|
||||
return SHStrDup(localized_context_menu_item.c_str(), name);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP template_folder_context_menu_item::GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon)
|
||||
{
|
||||
return SHStrDup(utilities::get_open_templates_icon_resource_filepath(module_instance_handle, ThemeHelpers::GetAppTheme()).c_str(), icon);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP template_folder_context_menu_item::Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept
|
||||
{
|
||||
Logger::info(L"Open templates 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;
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include "template_folder.h"
|
||||
#include "template_item.h"
|
||||
#include "new_utilities.h"
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
// The sub-context-menu that displays the list of templates
|
||||
class shell_context_sub_menu_item : public RuntimeClass<RuntimeClassFlags<ClassicCom>, IExplorerCommand>
|
||||
{
|
||||
public:
|
||||
shell_context_sub_menu_item(const template_item* template_entry, const ComPtr<IUnknown> site_of_folder);
|
||||
|
||||
// IExplorerCommand
|
||||
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* title);
|
||||
|
||||
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon);
|
||||
|
||||
IFACEMETHODIMP GetToolTip(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* infoTip);
|
||||
|
||||
IFACEMETHODIMP GetCanonicalName(_Out_ GUID* guidCommandName);
|
||||
|
||||
IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* selection, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* returned_state);
|
||||
|
||||
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept;
|
||||
|
||||
IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* returned_flags);
|
||||
|
||||
IFACEMETHODIMP EnumSubCommands(_COM_Outptr_ IEnumExplorerCommand** enumCommands);
|
||||
|
||||
protected:
|
||||
shell_context_sub_menu_item();
|
||||
const template_item* template_entry;
|
||||
ComPtr<IUnknown> site_of_folder;
|
||||
};
|
||||
|
||||
// Sub-context-menu separator between the list of templates menu-items and "Open templates" menu-item
|
||||
class separator_context_menu_item final : public shell_context_sub_menu_item
|
||||
{
|
||||
public:
|
||||
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* title);
|
||||
|
||||
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon);
|
||||
|
||||
IFACEMETHODIMP GetFlags(_Out_ EXPCMDFLAGS* returned_flags);
|
||||
};
|
||||
|
||||
// Sub-context-menu - The "Open templates" menu-item
|
||||
class template_folder_context_menu_item final : public shell_context_sub_menu_item
|
||||
{
|
||||
public:
|
||||
template_folder_context_menu_item(const std::filesystem::path shell_template_folder);
|
||||
|
||||
IFACEMETHODIMP GetTitle(_In_opt_ IShellItemArray* items, _Outptr_result_nullonfailure_ PWSTR* name);
|
||||
|
||||
IFACEMETHODIMP GetIcon(_In_opt_ IShellItemArray*, _Outptr_result_nullonfailure_ PWSTR* icon);
|
||||
|
||||
IFACEMETHODIMP Invoke(_In_opt_ IShellItemArray* selection, _In_opt_ IBindCtx*) noexcept;
|
||||
|
||||
std::filesystem::path shell_template_folder;
|
||||
};
|
||||
@@ -0,0 +1,52 @@
|
||||
#include "pch.h"
|
||||
#include <shellapi.h>
|
||||
#include "template_folder.h"
|
||||
|
||||
using namespace newplus;
|
||||
|
||||
template_folder::template_folder(){};
|
||||
|
||||
template_folder::template_folder(const std::filesystem::path newplus_template_folder)
|
||||
{
|
||||
this->template_folder_path = newplus_template_folder;
|
||||
}
|
||||
|
||||
void template_folder::init()
|
||||
{
|
||||
rescan_template_folder();
|
||||
}
|
||||
|
||||
void template_folder::rescan_template_folder()
|
||||
{
|
||||
list_of_templates.clear();
|
||||
|
||||
std::list<std::pair<std::wstring, template_item*>> dirs;
|
||||
std::list<std::pair<std::wstring, template_item*>> files;
|
||||
for (const auto& entry : std::filesystem::directory_iterator(template_folder_path))
|
||||
{
|
||||
if (entry.is_directory())
|
||||
{
|
||||
dirs.push_back({ entry.path().wstring(), new template_item(entry) });
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!utilities::is_hidden(entry.path()))
|
||||
{
|
||||
files.push_back({ entry.path().wstring(), new template_item(entry) });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// List of templates are sorted, with template-directories/folders first then followed by template-files
|
||||
dirs.sort();
|
||||
files.sort();
|
||||
list_of_templates = dirs;
|
||||
list_of_templates.splice(list_of_templates.end(), files);
|
||||
}
|
||||
|
||||
template_item* template_folder::get_template_item(const int index) const
|
||||
{
|
||||
auto it = list_of_templates.begin();
|
||||
std::advance(it, index);
|
||||
return it->second;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <list>
|
||||
#include "template_item.h"
|
||||
|
||||
namespace newplus
|
||||
{
|
||||
class template_folder
|
||||
{
|
||||
public:
|
||||
template_folder(const std::filesystem::path newplus_template_folder);
|
||||
void rescan_template_folder();
|
||||
|
||||
std::filesystem::path template_folder_path;
|
||||
std::list<std::pair<std::wstring, template_item*>> list_of_templates;
|
||||
|
||||
template_item* get_template_item(const int index) const;
|
||||
|
||||
protected:
|
||||
template_folder();
|
||||
void init();
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
|
||||
|
||||
#include "pch.h"
|
||||
#include "template_item.h"
|
||||
#include <shellapi.h>
|
||||
#include "new_utilities.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj_core.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
using namespace newplus;
|
||||
|
||||
template_item::template_item(const std::filesystem::path entry)
|
||||
{
|
||||
path = entry;
|
||||
}
|
||||
|
||||
std::wstring template_item::get_menu_title(const bool show_extension, const bool show_starting_digits) const
|
||||
{
|
||||
std::wstring title = path.filename();
|
||||
|
||||
if (!show_starting_digits)
|
||||
{
|
||||
// Hide starting digits, spaces, and .
|
||||
title = remove_starting_digits_from_filename(title);
|
||||
}
|
||||
|
||||
if (show_extension || !path.has_extension())
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
std::wstring ext = path.extension();
|
||||
title = title.substr(0, title.length() - ext.length());
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
std::wstring template_item::get_target_filename(const bool include_starting_digits) const
|
||||
{
|
||||
std::wstring filename = path.filename();
|
||||
|
||||
if (!include_starting_digits)
|
||||
{
|
||||
// Remove starting digits, spaces, and .
|
||||
filename = remove_starting_digits_from_filename(filename);
|
||||
}
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
std::wstring template_item::remove_starting_digits_from_filename(std::wstring filename) const
|
||||
{
|
||||
filename.erase(0, min(filename.find_first_not_of(L"0123456789 ."), filename.size()));
|
||||
|
||||
return filename;
|
||||
}
|
||||
|
||||
std::wstring template_item::get_explorer_icon() const
|
||||
{
|
||||
return utilities::get_explorer_icon(path);
|
||||
}
|
||||
|
||||
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,
|
||||
wchar_t double_terminated_path_from[MAX_PATH + 1] = { 0 };
|
||||
wcsncpy_s(double_terminated_path_from, this->path.c_str(), this->path.string().length());
|
||||
double_terminated_path_from[this->path.string().length() + 1] = 0;
|
||||
|
||||
wchar_t double_terminated_path_to[MAX_PATH + 1] = { 0 };
|
||||
wcsncpy_s(double_terminated_path_to, destination.c_str(), destination.string().length());
|
||||
double_terminated_path_to[destination.string().length() + 1] = 0;
|
||||
|
||||
SHFILEOPSTRUCT file_operation_params = { 0 };
|
||||
file_operation_params.wFunc = FO_COPY;
|
||||
file_operation_params.hwnd = window_handle;
|
||||
file_operation_params.pFrom = double_terminated_path_from;
|
||||
file_operation_params.pTo = double_terminated_path_to;
|
||||
file_operation_params.fFlags = FOF_RENAMEONCOLLISION | FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS | FOF_WANTMAPPINGHANDLE;
|
||||
|
||||
const int result = SHFileOperation(&file_operation_params);
|
||||
|
||||
if (!file_operation_params.hNameMappings)
|
||||
{
|
||||
// No file name collision on copy
|
||||
return destination;
|
||||
}
|
||||
|
||||
struct file_operation_collision_mapping
|
||||
{
|
||||
int index;
|
||||
SHNAMEMAPPING* mapping;
|
||||
};
|
||||
|
||||
file_operation_collision_mapping* mapping = static_cast<file_operation_collision_mapping*>(file_operation_params.hNameMappings);
|
||||
SHNAMEMAPPING* map = &mapping->mapping[0];
|
||||
std::wstring final_path(map->pszNewPath);
|
||||
|
||||
SHFreeNameMappings(file_operation_params.hNameMappings);
|
||||
|
||||
return final_path;
|
||||
}
|
||||
|
||||
void template_item::enter_rename_mode(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath) const
|
||||
{
|
||||
std::thread thread_for_renaming_workaround(rename_on_other_thread_workaround, site, target_fullpath);
|
||||
thread_for_renaming_workaround.detach();
|
||||
}
|
||||
|
||||
void template_item::rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath)
|
||||
{
|
||||
// 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.
|
||||
const std::chrono::milliseconds approx_wait_for_icon_redraw_not_needed{ 350 };
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(approx_wait_for_icon_redraw_not_needed));
|
||||
|
||||
const std::wstring filename = target_fullpath.filename();
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
|
||||
namespace newplus
|
||||
{
|
||||
class template_item
|
||||
{
|
||||
public:
|
||||
template_item(const std::filesystem::path entry);
|
||||
|
||||
std::wstring get_menu_title(const bool show_extension, const bool show_starting_digits) const;
|
||||
|
||||
std::wstring get_target_filename(const bool include_starting_digits) const;
|
||||
|
||||
std::wstring get_explorer_icon() 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;
|
||||
|
||||
std::filesystem::path path;
|
||||
|
||||
private:
|
||||
static void rename_on_other_thread_workaround(const ComPtr<IUnknown> site, const std::filesystem::path target_fullpath);
|
||||
|
||||
std::wstring remove_starting_digits_from_filename(std::wstring filename) const;
|
||||
};
|
||||
}
|
||||
70
src/modules/NewPlus/NewShellExtensionContextMenu/trace.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "trace.h"
|
||||
#include <common/Telemetry/ProjectTelemetry.h>
|
||||
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_hProvider,
|
||||
"Microsoft.PowerToys",
|
||||
// {38e8889b-9731-53f5-e901-e8a7c1753074}
|
||||
(0x38e8889b, 0x9731, 0x53f5, 0xe9, 0x01, 0xe8, 0xa7, 0xc1, 0x75, 0x30, 0x74),
|
||||
TraceLoggingOptionProjectTelemetry());
|
||||
|
||||
void Trace::RegisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingRegister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::UnregisterProvider() noexcept
|
||||
{
|
||||
TraceLoggingUnregister(g_hProvider);
|
||||
}
|
||||
|
||||
void Trace::EventToggleOnOff(_In_ const bool enabled) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"NewPlus_EventToggleOnOff",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(enabled, "Enabled"));
|
||||
}
|
||||
|
||||
void Trace::EventChangedTemplateLocation() noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"NewPlus_ChangedTemplateLocation",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::EventShowTemplateItems(const size_t number_of_templates) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"NewPlus_EventShowTemplateItems",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingValue(number_of_templates, "Count"));
|
||||
}
|
||||
|
||||
void Trace::EventCopyTemplate(_In_ const std::wstring template_file_extension) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"NewPlus_EventCopyTemplate",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingWideString(template_file_extension.c_str(), "Ext"));
|
||||
}
|
||||
|
||||
void Trace::EventCopyTemplateResult(_In_ const HRESULT hr) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"NewPlus_EventCopyTemplateResult",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
15
src/modules/NewPlus/NewShellExtensionContextMenu/trace.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
class Trace
|
||||
{
|
||||
public:
|
||||
static void RegisterProvider() noexcept;
|
||||
static void UnregisterProvider() noexcept;
|
||||
static void EventToggleOnOff(_In_ const bool new_enabled_state) noexcept;
|
||||
static void EventChangedTemplateLocation() 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 EventCopyTemplateResult(_In_ const HRESULT hr) noexcept;
|
||||
};
|
||||
@@ -49,7 +49,7 @@ static ThemeListener theme_listener{};
|
||||
static void handleTheme()
|
||||
{
|
||||
auto theme = theme_listener.AppTheme;
|
||||
auto isDark = theme == AppTheme::Dark;
|
||||
auto isDark = theme == Theme::Dark;
|
||||
Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
|
||||
if (hwndEditKeyboardNativeWindow != nullptr)
|
||||
{
|
||||
@@ -137,7 +137,7 @@ inline void CreateEditKeyboardWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMan
|
||||
windowClass.lpfnWndProc = EditKeyboardWindowProc;
|
||||
windowClass.hInstance = hInst;
|
||||
windowClass.lpszClassName = szWindowClass;
|
||||
windowClass.hbrBackground = CreateSolidBrush((ThemeHelpers::GetAppTheme() == AppTheme::Dark) ? 0x00000000 : 0x00FFFFFF);
|
||||
windowClass.hbrBackground = CreateSolidBrush((ThemeHelpers::GetAppTheme() == Theme::Dark) ? 0x00000000 : 0x00FFFFFF);
|
||||
windowClass.hIcon = static_cast<HICON>(LoadImageW(
|
||||
windowClass.hInstance,
|
||||
MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON),
|
||||
|
||||
@@ -43,7 +43,7 @@ static ThemeListener theme_listener{};
|
||||
static void handleTheme()
|
||||
{
|
||||
auto theme = theme_listener.AppTheme;
|
||||
auto isDark = theme == AppTheme::Dark;
|
||||
auto isDark = theme == Theme::Dark;
|
||||
Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
|
||||
if (hwndEditShortcutsNativeWindow != nullptr)
|
||||
{
|
||||
@@ -90,7 +90,7 @@ inline void CreateEditShortcutsWindowImpl(HINSTANCE hInst, KBMEditor::KeyboardMa
|
||||
windowClass.lpfnWndProc = EditShortcutsWindowProc;
|
||||
windowClass.hInstance = hInst;
|
||||
windowClass.lpszClassName = szWindowClass;
|
||||
windowClass.hbrBackground = CreateSolidBrush((ThemeHelpers::GetAppTheme() == AppTheme::Dark) ? 0x00000000 : 0x00FFFFFF);
|
||||
windowClass.hbrBackground = CreateSolidBrush((ThemeHelpers::GetAppTheme() == Theme::Dark) ? 0x00000000 : 0x00FFFFFF);
|
||||
windowClass.hIcon = static_cast<HICON>(LoadImageW(
|
||||
windowClass.hInstance,
|
||||
MAKEINTRESOURCE(IDS_KEYBOARDMANAGER_ICON),
|
||||
|
||||
@@ -43,7 +43,7 @@ HWND CurrentWindow;
|
||||
void handleTheme()
|
||||
{
|
||||
auto theme = theme_listener.AppTheme;
|
||||
auto isDark = theme == AppTheme::Dark;
|
||||
auto isDark = theme == Theme::Dark;
|
||||
Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
|
||||
ThemeHelpers::SetImmersiveDarkMode(CurrentWindow, isDark);
|
||||
}
|
||||
|
||||