mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 09:46:54 +02:00
[New+]Support for variables in template filenames (#37074)
* Add variable support - initial version without UI * Add variable in template filename support in New+ * Fix XAML style * Addressed code review feedback
This commit is contained in:
committed by
GitHub
parent
1d358af600
commit
1f81d14000
@@ -232,6 +232,10 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getAllowDataDiagnosticsValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredNewPlusReplaceVariablesValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredNewPlusReplaceVariablesValue());
|
||||
}
|
||||
GpoRuleConfigured GPOWrapper::GetConfiguredRunAtStartupValue()
|
||||
{
|
||||
return static_cast<GpoRuleConfigured>(powertoys_gpo::getConfiguredRunAtStartupValue());
|
||||
|
||||
@@ -64,6 +64,7 @@ namespace winrt::PowerToys::GPOWrapper::implementation
|
||||
static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue();
|
||||
static GpoRuleConfigured GetAllowDataDiagnosticsValue();
|
||||
static GpoRuleConfigured GetConfiguredRunAtStartupValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue();
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ namespace PowerToys
|
||||
static GpoRuleConfigured GetConfiguredNewPlusHideTemplateFilenameExtensionValue();
|
||||
static GpoRuleConfigured GetAllowDataDiagnosticsValue();
|
||||
static GpoRuleConfigured GetConfiguredRunAtStartupValue();
|
||||
static GpoRuleConfigured GetConfiguredNewPlusReplaceVariablesValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,6 +86,7 @@ namespace powertoys_gpo {
|
||||
const std::wstring POLICY_MWB_DISABLE_USER_DEFINED_IP_MAPPING_RULES = L"MwbDisableUserDefinedIpMappingRules";
|
||||
const std::wstring POLICY_MWB_POLICY_DEFINED_IP_MAPPING_RULES = L"MwbPolicyDefinedIpMappingRules";
|
||||
const std::wstring POLICY_NEW_PLUS_HIDE_TEMPLATE_FILENAME_EXTENSION = L"NewPlusHideTemplateFilenameExtension";
|
||||
const std::wstring POLICY_NEW_PLUS_REPLACE_VARIABLES = L"NewPlusReplaceVariablesInTemplateFilenames";
|
||||
|
||||
// Methods used for reading the registry
|
||||
#pragma region ReadRegistryMethods
|
||||
@@ -609,5 +610,11 @@ namespace powertoys_gpo {
|
||||
{
|
||||
return getConfiguredValue(POLICY_NEW_PLUS_HIDE_TEMPLATE_FILENAME_EXTENSION);
|
||||
}
|
||||
|
||||
inline gpo_rule_configured_t getConfiguredNewPlusReplaceVariablesValue()
|
||||
{
|
||||
return getConfiguredValue(POLICY_NEW_PLUS_REPLACE_VARIABLES);
|
||||
}
|
||||
|
||||
#pragma endregion IndividualModuleSettingPolicies
|
||||
}
|
||||
|
||||
@@ -671,5 +671,15 @@
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
<policy name="NewPlusReplaceVariablesInTemplateFilenames" class="Both" displayName="$(string.NewPlusReplaceVariablesInTemplateFilenames)" explainText="$(string.NewPlusReplaceVariablesInTemplateFilenamesDescription)" key="Software\Policies\PowerToys" valueName="NewPlusReplaceVariablesInTemplateFilenames">
|
||||
<parentCategory ref="NewPlus" />
|
||||
<supportedOn ref="SUPPORTED_POWERTOYS_0_89_0" />
|
||||
<enabledValue>
|
||||
<decimal value="1" />
|
||||
</enabledValue>
|
||||
<disabledValue>
|
||||
<decimal value="0" />
|
||||
</disabledValue>
|
||||
</policy>
|
||||
</policies>
|
||||
</policyDefinitions>
|
||||
|
||||
@@ -226,6 +226,14 @@ If you enable this policy, the setting is enabled and the extension is hidden.
|
||||
If you disable this policy, the setting is disabled and the extension is shown.
|
||||
|
||||
If you don't configure this policy, the user takes control over the setting and can enable or disable it.
|
||||
</string>
|
||||
<string id="NewPlusReplaceVariablesInTemplateFilenamesDescription">This policy configures if supported variables will get replaced in template filenames.
|
||||
|
||||
If you enable this policy, the setting is enabled and supported variables in filenames will get replaced.
|
||||
|
||||
If you disable this policy, the setting is disabled and variables in filenames will not get replaced.
|
||||
|
||||
If you don't configure this policy, the user will be able to control the setting and can enable or disable it.
|
||||
</string>
|
||||
|
||||
<string id="ConfigureAllUtilityGlobalEnabledState">Configure global utility enabled state</string>
|
||||
@@ -289,7 +297,7 @@ If you don't configure this policy, the user takes control over the setting and
|
||||
<string id="MwbPolicyDefinedIpMappingRules">Predefined IP Address mapping rules</string>
|
||||
<string id="NewPlusHideTemplateFilenameExtension">Hide template filename extension</string>
|
||||
<string id="AllowDiagnosticData">Allow sending diagnostic data</string>
|
||||
<string id="ConfigureRunAtStartup">Configure the run at startup setting</string>
|
||||
<string id="ConfigureRunAtStartup">Configure the run at startup setting</string>
|
||||
</stringTable>
|
||||
|
||||
<presentationTable>
|
||||
|
||||
@@ -83,6 +83,8 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\constants.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\helpers_filesystem.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\helpers_variables.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\new_utilities.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h" />
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\shell_context_sub_menu.h" />
|
||||
@@ -97,6 +99,7 @@
|
||||
<ClInclude Include="shell_context_menu_win10.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\powertoys_module.cpp" />
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\settings.cpp" />
|
||||
|
||||
@@ -57,6 +57,12 @@
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\settings.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\helpers_filesystem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\NewShellExtensionContextMenu\helpers_variables.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -92,6 +98,9 @@
|
||||
<ClCompile Include="..\NewShellExtensionContextMenu\new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="Generated Files\new.rc">
|
||||
|
||||
@@ -32,7 +32,7 @@ IFACEMETHODIMP shell_context_menu_win10::Initialize(PCIDLIST_ABSOLUTE, IDataObje
|
||||
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()
|
||||
|| package::IsWin11OrGreater()
|
||||
)
|
||||
{
|
||||
return E_FAIL;
|
||||
@@ -47,7 +47,7 @@ IFACEMETHODIMP shell_context_menu_win10::QueryContextMenu(HMENU menu_handle, UIN
|
||||
{
|
||||
// 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;
|
||||
MENUITEMINFO newplus_main_context_menu_item = { 0 };
|
||||
HMENU sub_menu_of_templates = CreatePopupMenu();
|
||||
int sub_menu_index = 0;
|
||||
|
||||
@@ -142,7 +142,7 @@ void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu
|
||||
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;
|
||||
MENUITEMINFO newplus_menu_item_open_templates = { 0 };
|
||||
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;
|
||||
@@ -174,7 +174,7 @@ void shell_context_menu_win10::add_open_templates_to_context_menu(HMENU sub_menu
|
||||
|
||||
void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_templates, int sub_menu_index)
|
||||
{
|
||||
MENUITEMINFO menu_item_separator;
|
||||
MENUITEMINFO menu_item_separator = { 0 };
|
||||
menu_item_separator.cbSize = sizeof(MENUITEMINFO);
|
||||
menu_item_separator.fMask = MIIM_FTYPE;
|
||||
menu_item_separator.fType = MFT_SEPARATOR;
|
||||
@@ -184,8 +184,11 @@ void shell_context_menu_win10::add_separator_to_context_menu(HMENU sub_menu_of_t
|
||||
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;
|
||||
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(),
|
||||
utilities::get_newplus_setting_resolve_variables()).c_str());
|
||||
MENUITEMINFO newplus_menu_item_template = { 0 };
|
||||
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;
|
||||
|
||||
@@ -114,6 +114,8 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="dll_main.h" />
|
||||
<ClInclude Include="helpers_filesystem.h" />
|
||||
<ClInclude Include="helpers_variables.h" />
|
||||
<ClInclude Include="shell_context_menu.h" />
|
||||
<ClInclude Include="shell_context_sub_menu.h" />
|
||||
<ClInclude Include="shell_context_sub_menu_item.h" />
|
||||
@@ -128,6 +130,7 @@ MakeAppx.exe pack /d . /p $(OutDir)NewPlusPackage.msix /nv</Command>
|
||||
<ClInclude Include="template_item.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp" />
|
||||
<ClCompile Include="new_utilities.cpp" />
|
||||
<ClCompile Include="shell_context_menu.cpp" />
|
||||
<ClCompile Include="shell_context_sub_menu.cpp" />
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
<ClCompile Include="new_utilities.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\powerrename\lib\Helpers.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="template_folder.h">
|
||||
@@ -75,6 +78,12 @@
|
||||
<ClInclude Include="resource.base.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="helpers_filesystem.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="helpers_variables.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace newplus::constants::non_localizable
|
||||
|
||||
constexpr WCHAR settings_json_key_hide_starting_digits[] = L"HideStartingDigits";
|
||||
|
||||
constexpr WCHAR settings_json_key_replace_variables[] = L"ReplaceVariables";
|
||||
|
||||
constexpr WCHAR settings_json_key_template_location[] = L"TemplateLocation";
|
||||
|
||||
constexpr WCHAR context_menu_package_name[] = L"NewPlusContextMenu";
|
||||
@@ -30,5 +32,5 @@ namespace newplus::constants::non_localizable
|
||||
|
||||
constexpr WCHAR open_templates_icon_dark_resource_relative_path[] = L"\\Assets\\NewPlus\\Open_templates_dark.ico";
|
||||
|
||||
constexpr WCHAR desktop_ini_filename[] = L"desktop.ini";
|
||||
constexpr WCHAR parent_folder_name_variable[] = L"$PARENT_FOLDER_NAME";
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include "helpers_variables.h"
|
||||
|
||||
namespace newplus::helpers::filesystem
|
||||
{
|
||||
namespace constants::non_localizable
|
||||
{
|
||||
constexpr WCHAR desktop_ini_filename[] = L"desktop.ini";
|
||||
}
|
||||
|
||||
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 is_directory(const std::filesystem::path path)
|
||||
{
|
||||
const auto entry = std::filesystem::directory_entry(path);
|
||||
return entry.is_directory();
|
||||
}
|
||||
|
||||
inline std::wstring make_valid_filename(const std::wstring& string, const wchar_t replace_with = L' ')
|
||||
{
|
||||
// replace all non-filename-valid chars with replace_with wchar
|
||||
std::wstring valid_filename = string;
|
||||
|
||||
std::replace_if(valid_filename.begin(), valid_filename.end(), [](wchar_t c) { return c == L'/' || c == L'\\' || c == L':' || c == L'*' || c == L'?' || c == L'"' || c == L'<' || c == L'>' || c == L'|'; }, replace_with);
|
||||
|
||||
return valid_filename;
|
||||
}
|
||||
|
||||
inline std::wstring make_unique_path_name(const std::wstring& initial_path)
|
||||
{
|
||||
std::filesystem::path folder_path(initial_path);
|
||||
std::filesystem::path path_based_on(initial_path);
|
||||
|
||||
int counter = 1;
|
||||
|
||||
while (std::filesystem::exists(folder_path))
|
||||
{
|
||||
std::wstring new_filename = path_based_on.stem().wstring() + L" (" + std::to_wstring(counter) + L")";
|
||||
if (path_based_on.has_extension())
|
||||
{
|
||||
new_filename += path_based_on.extension().wstring();
|
||||
}
|
||||
folder_path = path_based_on.parent_path() / new_filename;
|
||||
counter++;
|
||||
}
|
||||
|
||||
return folder_path.wstring();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
#pragma once
|
||||
|
||||
#include <regex>
|
||||
#include "..\..\powerrename\lib\Helpers.h"
|
||||
#include "helpers_filesystem.h"
|
||||
|
||||
#pragma comment(lib, "Pathcch.lib")
|
||||
|
||||
namespace newplus::helpers::variables
|
||||
{
|
||||
inline std::wstring resolve_an_environment_variable(const std::wstring& string)
|
||||
{
|
||||
std::wstring return_string = string;
|
||||
wchar_t* env_variable = nullptr;
|
||||
|
||||
_wdupenv_s(&env_variable, nullptr, return_string.c_str());
|
||||
|
||||
if (env_variable != nullptr)
|
||||
{
|
||||
return_string = env_variable;
|
||||
free(env_variable);
|
||||
}
|
||||
|
||||
return return_string;
|
||||
}
|
||||
|
||||
inline std::wstring resolve_date_time_variables(const std::wstring& string)
|
||||
{
|
||||
SYSTEMTIME local_now = { 0 };
|
||||
GetLocalTime(&local_now);
|
||||
wchar_t resolved_filename[MAX_PATH] = { 0 };
|
||||
GetDatedFileName(resolved_filename, ARRAYSIZE(resolved_filename), string.c_str(), local_now);
|
||||
|
||||
return resolved_filename;
|
||||
}
|
||||
|
||||
inline std::wstring replace_all_occurrences(const std::wstring& string, const std::wstring& search_for, const std::wstring& replacement)
|
||||
{
|
||||
std::wstring return_string = string;
|
||||
size_t pos = 0;
|
||||
|
||||
while ((pos = return_string.find(search_for, pos)) != std::wstring::npos)
|
||||
{
|
||||
return_string.replace(pos, search_for.length(), replacement);
|
||||
pos += replacement.length();
|
||||
}
|
||||
|
||||
return return_string;
|
||||
}
|
||||
|
||||
inline std::wstring resolve_environment_variables(const std::wstring& string)
|
||||
{
|
||||
// Do case-insensitive string replacement of environment variables being consistent with normal %eNV_VaR% behavior
|
||||
std::wstring return_string = string;
|
||||
const std::wregex reg_expression(L"%([^%]+)%");
|
||||
std::wsmatch match;
|
||||
|
||||
size_t start = 0;
|
||||
while (std::regex_search(return_string.cbegin() + start, return_string.cend(), match, reg_expression))
|
||||
{
|
||||
std::wstring env_var_name = match[1].str();
|
||||
std::wstring env_var_value = resolve_an_environment_variable(env_var_name);
|
||||
if (!env_var_value.empty())
|
||||
{
|
||||
size_t match_position = match.position(0) + start;
|
||||
return_string.replace(match_position, match.length(0), env_var_value);
|
||||
start = match_position + env_var_value.length();
|
||||
}
|
||||
else
|
||||
{
|
||||
start += match.position(0) + match.length(0);
|
||||
}
|
||||
}
|
||||
|
||||
return return_string;
|
||||
}
|
||||
|
||||
inline std::wstring resolve_parent_folder(const std::wstring& string, const std::wstring& parent_folder_name)
|
||||
{
|
||||
// Do case-sensitive string replacement, for consistency on variables designated with $
|
||||
std::wstring result = replace_all_occurrences(string, constants::non_localizable::parent_folder_name_variable, parent_folder_name);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::filesystem::path resolve_variables_in_filename(const std::wstring& filename, const std::wstring& parent_folder_name)
|
||||
{
|
||||
std::wstring result;
|
||||
|
||||
result = resolve_date_time_variables(filename);
|
||||
result = resolve_environment_variables(result);
|
||||
if (!parent_folder_name.empty())
|
||||
{
|
||||
result = resolve_parent_folder(result, parent_folder_name);
|
||||
}
|
||||
result = newplus::helpers::filesystem::make_valid_filename(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline std::filesystem::path resolve_variables_in_path(const std::filesystem::path& path)
|
||||
{
|
||||
// Need to resolve the whole path top-down (root to leaf), because of the support for $PARENT_FOLDER_NAME
|
||||
std::filesystem::path result;
|
||||
std::wstring previous_section;
|
||||
std::wstring current_section;
|
||||
auto path_section = path.begin();
|
||||
int level = 0;
|
||||
|
||||
while (path_section != path.end())
|
||||
{
|
||||
previous_section = current_section;
|
||||
current_section = path_section->wstring();
|
||||
|
||||
if (level <= 1)
|
||||
{
|
||||
// Up to and including L"x:\\"
|
||||
result /= current_section;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Past L"x:\\", e.g. L"x:\\level1" and beyond
|
||||
result /= resolve_variables_in_filename(current_section, previous_section);
|
||||
}
|
||||
path_section++;
|
||||
level++;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
inline void resolve_variables_in_filename_and_rename_files(const std::filesystem::path& path, const bool do_rename = true)
|
||||
{
|
||||
// Depth first recursion, so that we start renaming the leaves, and avoid having to rescan
|
||||
for (const auto& entry : std::filesystem::directory_iterator(path))
|
||||
{
|
||||
if (std::filesystem::is_directory(entry.status()))
|
||||
{
|
||||
resolve_variables_in_filename_and_rename_files(entry.path(), do_rename);
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the actual rename
|
||||
for (const auto& current : std::filesystem::directory_iterator(path))
|
||||
{
|
||||
if (!newplus::helpers::filesystem::is_hidden(current))
|
||||
{
|
||||
const std::filesystem::path resolved_path = resolve_variables_in_path(current.path());
|
||||
|
||||
// Only rename if the filename is actually different
|
||||
const std::wstring non_resolved_leaf = current.path().filename();
|
||||
const std::wstring resolved_leaf = resolved_path.filename();
|
||||
|
||||
if (StrCmpIW(non_resolved_leaf.c_str(), resolved_leaf.c_str()) != 0)
|
||||
{
|
||||
const std::wstring org_name = current.path();
|
||||
const std::wstring new_name = current.path().parent_path() / resolved_leaf;
|
||||
const std::wstring really_new_name = helpers::filesystem::make_unique_path_name(new_name);
|
||||
|
||||
// To aid with testing, only conditionally rename
|
||||
if (do_rename)
|
||||
{
|
||||
std::filesystem::rename(org_name, really_new_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "settings.h"
|
||||
#include "template_item.h"
|
||||
#include "trace.h"
|
||||
#include "helpers_variables.h"
|
||||
|
||||
#pragma comment(lib, "Shlwapi.lib")
|
||||
|
||||
@@ -72,23 +73,6 @@ namespace newplus::utilities
|
||||
return hIcon;
|
||||
}
|
||||
|
||||
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 is_directory(const std::filesystem::path path)
|
||||
{
|
||||
const auto entry = std::filesystem::directory_entry(path);
|
||||
return entry.is_directory();
|
||||
}
|
||||
|
||||
inline bool wstring_same_when_comparing_ignore_case(std::wstring stringA, std::wstring stringB)
|
||||
{
|
||||
transform(stringA.begin(), stringA.end(), stringA.begin(), towupper);
|
||||
@@ -97,20 +81,6 @@ namespace newplus::utilities
|
||||
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();
|
||||
@@ -126,6 +96,11 @@ namespace newplus::utilities
|
||||
return NewSettingsInstance().GetHideStartingDigits();
|
||||
}
|
||||
|
||||
inline bool get_newplus_setting_resolve_variables()
|
||||
{
|
||||
return NewSettingsInstance().GetReplaceVariables();
|
||||
}
|
||||
|
||||
inline void create_folder_if_not_exist(const std::filesystem::path path)
|
||||
{
|
||||
std::filesystem::create_directory(path);
|
||||
@@ -259,6 +234,7 @@ namespace newplus::utilities
|
||||
{
|
||||
ComPtr<IWebBrowserApp> web_browser_app;
|
||||
VARIANT v;
|
||||
VariantInit(&v);
|
||||
V_VT(&v) = VT_I4;
|
||||
V_I4(&v) = i;
|
||||
hr = shell_windows->Item(v, &shell_window);
|
||||
@@ -382,15 +358,30 @@ namespace newplus::utilities
|
||||
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))
|
||||
// Get target name without starting digits as appropriate
|
||||
const std::wstring target_name = template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits());
|
||||
|
||||
// Get initial resolved name
|
||||
target_fullpath /= target_name;
|
||||
|
||||
// Expand variables in name of the target path
|
||||
if (utilities::get_newplus_setting_resolve_variables())
|
||||
{
|
||||
target_fullpath.append(template_entry->get_target_filename(!utilities::get_newplus_setting_hide_starting_digits()));
|
||||
target_fullpath = helpers::variables::resolve_variables_in_path(target_fullpath);
|
||||
}
|
||||
|
||||
// Copy file and determine final filename
|
||||
// See if our target already exist, and if so then generate a unique name
|
||||
target_fullpath = helpers::filesystem::make_unique_path_name(target_fullpath);
|
||||
|
||||
// Finally copy file/folder/subfolders
|
||||
std::filesystem::path target_final_fullpath = template_entry->copy_object_to(GetActiveWindow(), target_fullpath);
|
||||
|
||||
// Resolve variables and rename files in newly copied folders and subfolders and files
|
||||
if (utilities::get_newplus_setting_resolve_variables() && helpers::filesystem::is_directory(target_final_fullpath))
|
||||
{
|
||||
helpers::variables::resolve_variables_in_filename_and_rename_files(target_final_fullpath);
|
||||
}
|
||||
|
||||
// Touch all files and set last modified to "now"
|
||||
update_last_write_time(target_final_fullpath);
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ void NewSettings::Save()
|
||||
|
||||
values.add_property(newplus::constants::non_localizable::settings_json_key_hide_file_extension, new_settings.hide_file_extension);
|
||||
values.add_property(newplus::constants::non_localizable::settings_json_key_hide_starting_digits, new_settings.hide_starting_digits);
|
||||
values.add_property(newplus::constants::non_localizable::settings_json_key_replace_variables, new_settings.replace_variables);
|
||||
values.add_property(newplus::constants::non_localizable::settings_json_key_template_location, new_settings.template_location);
|
||||
|
||||
values.save_to_settings_file();
|
||||
@@ -70,6 +71,9 @@ void NewSettings::InitializeWithDefaultSettings()
|
||||
// Currently a similar defaulting logic is also in InitializeWithDefaultSettings in NewViewModel.cs
|
||||
SetHideFileExtension(true);
|
||||
|
||||
// By default Replace Variables is turned off
|
||||
SetReplaceVariables(false);
|
||||
|
||||
SetTemplateLocation(GetTemplateLocationDefaultPath());
|
||||
}
|
||||
|
||||
@@ -139,6 +143,12 @@ void NewSettings::ParseJson()
|
||||
new_settings.hide_starting_digits = hideStartingDigitsValue.value();
|
||||
}
|
||||
|
||||
auto resolveVariables = settings.get_bool_value(newplus::constants::non_localizable::settings_json_key_replace_variables);
|
||||
if (resolveVariables.has_value())
|
||||
{
|
||||
new_settings.replace_variables = resolveVariables.value();
|
||||
}
|
||||
|
||||
GetSystemTimeAsFileTime(&new_settings_last_loaded_timestamp);
|
||||
}
|
||||
|
||||
@@ -163,11 +173,8 @@ bool NewSettings::GetEnabled()
|
||||
|
||||
bool NewSettings::GetHideFileExtension() const
|
||||
{
|
||||
auto gpoSetting = powertoys_gpo::getConfiguredNewPlusHideTemplateFilenameExtensionValue();
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_enabled)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
const auto gpoSetting = powertoys_gpo::getConfiguredNewPlusHideTemplateFilenameExtensionValue();
|
||||
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
{
|
||||
return false;
|
||||
@@ -191,6 +198,23 @@ void NewSettings::SetHideStartingDigits(const bool hide_starting_digits)
|
||||
new_settings.hide_starting_digits = hide_starting_digits;
|
||||
}
|
||||
|
||||
bool NewSettings::GetReplaceVariables() const
|
||||
{
|
||||
const auto gpoSetting = powertoys_gpo::getConfiguredNewPlusReplaceVariablesValue();
|
||||
|
||||
if (gpoSetting == powertoys_gpo::gpo_rule_configured_disabled)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return new_settings.replace_variables;
|
||||
}
|
||||
|
||||
void NewSettings::SetReplaceVariables(const bool replace_variables)
|
||||
{
|
||||
new_settings.replace_variables = replace_variables;
|
||||
}
|
||||
|
||||
std::wstring NewSettings::GetTemplateLocation() const
|
||||
{
|
||||
return new_settings.template_location;
|
||||
@@ -201,7 +225,7 @@ void NewSettings::SetTemplateLocation(const std::wstring template_location)
|
||||
new_settings.template_location = template_location;
|
||||
}
|
||||
|
||||
std::wstring NewSettings::GetTemplateLocationDefaultPath()
|
||||
std::wstring NewSettings::GetTemplateLocationDefaultPath() const
|
||||
{
|
||||
static const std::wstring default_template_sub_folder_name =
|
||||
GET_RESOURCE_STRING_FALLBACK(
|
||||
|
||||
@@ -12,6 +12,8 @@ public:
|
||||
void SetHideFileExtension(const bool hide_file_extension);
|
||||
bool GetHideStartingDigits() const;
|
||||
void SetHideStartingDigits(const bool hide_starting_digits);
|
||||
bool GetReplaceVariables() const;
|
||||
void SetReplaceVariables(const bool resolve_variables);
|
||||
std::wstring GetTemplateLocation() const;
|
||||
void SetTemplateLocation(const std::wstring template_location);
|
||||
|
||||
@@ -25,12 +27,13 @@ private:
|
||||
bool enabled{ false };
|
||||
bool hide_file_extension{ true };
|
||||
bool hide_starting_digits{ true };
|
||||
bool replace_variables{ true };
|
||||
std::wstring template_location;
|
||||
};
|
||||
|
||||
void RefreshEnabledState();
|
||||
void InitializeWithDefaultSettings();
|
||||
std::wstring GetTemplateLocationDefaultPath();
|
||||
std::wstring GetTemplateLocationDefaultPath() const;
|
||||
|
||||
void Reload();
|
||||
void ParseJson();
|
||||
|
||||
@@ -69,8 +69,21 @@ IFACEMETHODIMP shell_context_menu::GetFlags(_Out_ EXPCMDFLAGS* returned_menu_ite
|
||||
|
||||
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));
|
||||
try
|
||||
{
|
||||
auto e = Make<shell_context_sub_menu>(site_of_folder);
|
||||
return e->QueryInterface(IID_PPV_ARGS(returned_enum_commands));
|
||||
}
|
||||
catch (const std::exception& ex)
|
||||
{
|
||||
Logger::error("New+ create submenu error: {}", ex.what());
|
||||
return E_FAIL;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
Logger::error("New+ create submenu error");
|
||||
return E_FAIL;
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -80,8 +93,8 @@ 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
|
||||
IFACEMETHODIMP shell_context_menu::GetSite(_In_ REFIID interface_type, _COM_Outptr_ void** returned_site) noexcept
|
||||
{
|
||||
return this->site_of_folder.CopyTo(riid, returned_site);
|
||||
return this->site_of_folder.CopyTo(interface_type, returned_site);
|
||||
}
|
||||
#pragma endregion
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
|
||||
#pragma region IObjectWithSite
|
||||
IFACEMETHODIMP SetSite(_In_ IUnknown* site) noexcept;
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID riid, _COM_Outptr_ void** site) noexcept;
|
||||
IFACEMETHODIMP GetSite(_In_ REFIID interface_type, _COM_Outptr_ void** site) noexcept;
|
||||
#pragma endregion
|
||||
|
||||
protected:
|
||||
|
||||
@@ -22,7 +22,8 @@ IFACEMETHODIMP shell_context_sub_menu_item::GetTitle(_In_opt_ IShellItemArray* i
|
||||
{
|
||||
return SHStrDup(this->template_entry->get_menu_title(
|
||||
!utilities::get_newplus_setting_hide_extension(),
|
||||
!utilities::get_newplus_setting_hide_starting_digits()
|
||||
!utilities::get_newplus_setting_hide_starting_digits(),
|
||||
utilities::get_newplus_setting_resolve_variables()
|
||||
).c_str(), title);
|
||||
}
|
||||
|
||||
@@ -95,6 +96,7 @@ IFACEMETHODIMP separator_context_menu_item::GetIcon(_In_opt_ IShellItemArray*, _
|
||||
|
||||
IFACEMETHODIMP separator_context_menu_item::GetFlags(_Out_ EXPCMDFLAGS* returned_flags)
|
||||
{
|
||||
// Separators no longer work on Windows 11 regular context menu. They do still work on the extended context menu.
|
||||
*returned_flags = ECF_ISSEPARATOR;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ void template_folder::rescan_template_folder()
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!utilities::is_hidden(entry.path()))
|
||||
if (!helpers::filesystem::is_hidden(entry.path()))
|
||||
{
|
||||
files.push_back({ entry.path().wstring(), new template_item(entry) });
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include "new_utilities.h"
|
||||
#include <cassert>
|
||||
#include <thread>
|
||||
#include <shellapi.h>
|
||||
#include <shlobj_core.h>
|
||||
|
||||
using namespace Microsoft::WRL;
|
||||
@@ -17,7 +16,7 @@ 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 template_item::get_menu_title(const bool show_extension, const bool show_starting_digits, const bool show_resolved_variables) const
|
||||
{
|
||||
std::wstring title = path.filename();
|
||||
|
||||
@@ -27,13 +26,21 @@ std::wstring template_item::get_menu_title(const bool show_extension, const bool
|
||||
title = remove_starting_digits_from_filename(title);
|
||||
}
|
||||
|
||||
if (show_resolved_variables)
|
||||
{
|
||||
title = helpers::variables::resolve_variables_in_filename(title, constants::non_localizable::parent_folder_name_variable);
|
||||
}
|
||||
|
||||
if (show_extension || !path.has_extension())
|
||||
{
|
||||
return title;
|
||||
}
|
||||
|
||||
std::wstring ext = path.extension();
|
||||
title = title.substr(0, title.length() - ext.length());
|
||||
if (!helpers::filesystem::is_directory(path))
|
||||
{
|
||||
std::wstring ext = path.extension();
|
||||
title = title.substr(0, title.length() - ext.length());
|
||||
}
|
||||
|
||||
return title;
|
||||
}
|
||||
@@ -53,7 +60,8 @@ std::wstring template_item::get_target_filename(const bool include_starting_digi
|
||||
|
||||
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()));
|
||||
filename.erase(0, min(filename.find_first_not_of(L"0123456789"), filename.size()));
|
||||
filename.erase(0, min(filename.find_first_not_of(L" ."), filename.size()));
|
||||
|
||||
return filename;
|
||||
}
|
||||
@@ -70,7 +78,7 @@ HICON template_item::get_explorer_icon_handle() 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.
|
||||
wchar_t double_terminated_path_from[MAX_PATH + 1] = { 0 };
|
||||
wcsncpy_s(double_terminated_path_from, this->path.c_str(), this->path.wstring().length());
|
||||
double_terminated_path_from[this->path.wstring().length() + 1] = 0;
|
||||
@@ -84,37 +92,16 @@ std::filesystem::path template_item::copy_object_to(const HWND window_handle, co
|
||||
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;
|
||||
file_operation_params.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR | FOF_NOCOPYSECURITYATTRIBS;
|
||||
|
||||
const int result = SHFileOperation(&file_operation_params);
|
||||
|
||||
if (!file_operation_params.hNameMappings)
|
||||
if (result != 0)
|
||||
{
|
||||
// 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;
|
||||
throw std::runtime_error("Failed to copy template");
|
||||
}
|
||||
|
||||
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;
|
||||
return destination;
|
||||
}
|
||||
|
||||
void template_item::refresh_target(const std::filesystem::path target_final_fullpath) const
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace newplus
|
||||
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_menu_title(const bool show_extension, const bool show_starting_digits, const bool show_resolved_variables) const;
|
||||
|
||||
std::wstring get_target_filename(const bool include_starting_digits) const;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
HideFileExtension = new BoolProperty(true);
|
||||
HideStartingDigits = new BoolProperty(true);
|
||||
TemplateLocation = new StringProperty(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "Microsoft", "PowerToys", "NewPlus", "Templates"));
|
||||
ReplaceVariables = new BoolProperty(false);
|
||||
}
|
||||
|
||||
[JsonPropertyName("HideFileExtension")]
|
||||
@@ -29,6 +30,9 @@ namespace Microsoft.PowerToys.Settings.UI.Library
|
||||
[JsonPropertyName("TemplateLocation")]
|
||||
public StringProperty TemplateLocation { get; set; }
|
||||
|
||||
[JsonPropertyName("ReplaceVariables")]
|
||||
public BoolProperty ReplaceVariables { get; set; }
|
||||
|
||||
public override string ToString() => JsonSerializer.Serialize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<Page
|
||||
<Page
|
||||
x:Class="Microsoft.PowerToys.Settings.UI.Views.NewPlusPage"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:controls="using:Microsoft.PowerToys.Settings.UI.Controls"
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Microsoft.PowerToys.Settings.UI.ViewModels"
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
xmlns:tkcontrols="using:CommunityToolkit.WinUI.Controls"
|
||||
xmlns:ui="using:CommunityToolkit.WinUI"
|
||||
@@ -84,6 +85,116 @@
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
</tkcontrols:SettingsCard>
|
||||
</controls:SettingsGroup>
|
||||
|
||||
<controls:SettingsGroup x:Uid="NewPlus_behavior" IsEnabled="{x:Bind ViewModel.IsEnabled, Mode=OneWay}">
|
||||
<tkcontrols:SettingsCard x:Uid="NewPlus_Behaviour_Replace_Variables_Toggle" IsEnabled="{x:Bind ViewModel.IsReplaceVariablesSettingsCardEnabled, Mode=OneWay}">
|
||||
<StackPanel Orientation="Horizontal" Spacing="4">
|
||||
<ToggleSwitch x:Uid="ReplaceVariablesToggle" IsOn="{x:Bind ViewModel.ReplaceVariables, Mode=TwoWay}" />
|
||||
<Button
|
||||
x:Uid="FileCreationButton"
|
||||
Width="28"
|
||||
Height="40"
|
||||
Margin="0,0,-4,0"
|
||||
Padding="0"
|
||||
HorizontalAlignment="Right"
|
||||
VerticalAlignment="Center"
|
||||
Background="Transparent"
|
||||
BorderBrush="Transparent"
|
||||
Content=""
|
||||
FontFamily="{ThemeResource SymbolThemeFontFamily}">
|
||||
<Button.Flyout>
|
||||
<Flyout x:Name="VariableExamplesFlyout" ShouldConstrainToRootBounds="False">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="62" />
|
||||
<ColumnDefinition Width="300" />
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="0"
|
||||
Text="$YYYY" />
|
||||
<TextBlock
|
||||
Grid.Row="0"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,5"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Year_YYYY_Variable_Description" /></TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="0"
|
||||
Text="$MM" />
|
||||
<TextBlock
|
||||
Grid.Row="1"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,5"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Month_MM_Variable_Description" /></TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="0"
|
||||
Text="$DD" />
|
||||
<TextBlock
|
||||
Grid.Row="2"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,5"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Day_DD_Variable_Description" /></TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="0"
|
||||
Text="$hh" />
|
||||
<TextBlock
|
||||
Grid.Row="3"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,5"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Hour_hh_Variable_Description" /></TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="0"
|
||||
Text="$mm" />
|
||||
<TextBlock
|
||||
Grid.Row="4"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,5"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Minute_mm_Variable_Description" /></TextBlock>
|
||||
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="0"
|
||||
Text="$ss" />
|
||||
<TextBlock
|
||||
Grid.Row="5"
|
||||
Grid.Column="1"
|
||||
Margin="0,0,0,0"
|
||||
TextWrapping="Wrap"><Run x:Uid="NewPlus_Second_ss_Variable_Description" /></TextBlock>
|
||||
</Grid>
|
||||
</Flyout>
|
||||
</Button.Flyout>
|
||||
</Button>
|
||||
</StackPanel>
|
||||
<tkcontrols:SettingsCard.Description>
|
||||
<StackPanel>
|
||||
<HyperlinkButton x:Uid="NewPlus_Behaviour_Replace_Variables_Learn_More" NavigateUri="https://aka.ms/PowerToysOverview_NewPlus" />
|
||||
</StackPanel>
|
||||
</tkcontrols:SettingsCard.Description>
|
||||
</tkcontrols:SettingsCard>
|
||||
<InfoBar
|
||||
x:Uid="GPO_SettingIsManaged"
|
||||
IsClosable="False"
|
||||
IsOpen="{x:Bind ViewModel.IsReplaceVariablesSettingGPOConfigured, Mode=OneWay}"
|
||||
IsTabStop="{x:Bind ViewModel.IsReplaceVariablesSettingGPOConfigured, Mode=OneWay}"
|
||||
Severity="Informational" />
|
||||
</controls:SettingsGroup>
|
||||
</StackPanel>
|
||||
</controls:SettingsPageControl.ModuleContent>
|
||||
|
||||
|
||||
@@ -2,9 +2,6 @@
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
|
||||
using Microsoft.PowerToys.Settings.UI.Helpers;
|
||||
using Microsoft.PowerToys.Settings.UI.Library;
|
||||
using Microsoft.PowerToys.Settings.UI.ViewModels;
|
||||
|
||||
@@ -4445,6 +4445,46 @@ Activate by holding the key for the character you want to add an accent to, then
|
||||
<value>This option is useful when using digits, spaces and dots at the beginning of filenames to control the display order of templates</value>
|
||||
<comment>Template filename starting digits settings toggle</comment>
|
||||
</data>
|
||||
<data name="NewPlus_behavior.Header" xml:space="preserve">
|
||||
<value>Behavior</value>
|
||||
<comment>New+ behavior related settings label</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Behaviour_Replace_Variables_Toggle.Header" xml:space="preserve">
|
||||
<value>Replace variables in template filename</value>
|
||||
<comment>New+ replace variables in template filename behavior toggle</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Behaviour_Replace_Variables_Learn_More.Content" xml:space="preserve">
|
||||
<value>Learn more about supported variables and see examples</value>
|
||||
<comment>New+ help link to learn more about supported variables and see examples</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Behaviour_Replace_Variables_Info_Card_Title.Text" xml:space="preserve">
|
||||
<value>Commonly used variables</value>
|
||||
<comment>New+ commonly used variables header in the flyout info card</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Year_YYYY_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Year, represented by a full four or five digits, depending on the calendar used.</value>
|
||||
<comment>New+ description of the year $YYYY variable - casing of $YYYY is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Month_MM_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Month, as digits with leading zeros for single-digit months.</value>
|
||||
<comment>New+ description of the month $MM variable - casing of $MM is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Day_DD_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Day of the month, as digits with leading zeros for single-digit days.</value>
|
||||
<comment>New+ description of the day $DD variable - casing of $DD is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Hour_hh_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Hours, with leading zeros for single-digit hours.</value>
|
||||
<comment>New+ description of the hour $hh variable - casing of $hh is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Minute_mm_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Minutes, with leading zeros for single-digit minutes.</value>
|
||||
<comment>New+ description of the minute $mm variable - casing of $mm is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus_Second_ss_Variable_Description.Text" xml:space="preserve">
|
||||
<value>Seconds, with leading zeros for single-digit seconds.</value>
|
||||
<comment>New+ description of the second $ss variable - casing of $ss is important</comment>
|
||||
</data>
|
||||
<data name="NewPlus.SecondaryLinksHeader" xml:space="preserve">
|
||||
<value>Attribution</value>
|
||||
<comment>giving credit</comment>
|
||||
|
||||
@@ -7,7 +7,6 @@ using System.Diagnostics;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows;
|
||||
using global::PowerToys.GPOWrapper;
|
||||
@@ -18,8 +17,6 @@ using Microsoft.PowerToys.Settings.UI.Library.Interfaces;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.Utilities;
|
||||
using Microsoft.PowerToys.Settings.UI.Library.ViewModels.Commands;
|
||||
using Microsoft.PowerToys.Settings.UI.SerializationContext;
|
||||
using Windows.ApplicationModel.VoiceCommands;
|
||||
using Windows.System;
|
||||
|
||||
using static Microsoft.PowerToys.Settings.UI.Helpers.ShellGetFolder;
|
||||
|
||||
@@ -50,6 +47,7 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
_hideFileExtension = Settings.Properties.HideFileExtension.Value;
|
||||
_hideStartingDigits = Settings.Properties.HideStartingDigits.Value;
|
||||
_templateLocation = Settings.Properties.TemplateLocation.Value;
|
||||
_replaceVariables = Settings.Properties.ReplaceVariables.Value;
|
||||
InitializeEnabledValue();
|
||||
InitializeGpoValues();
|
||||
|
||||
@@ -77,6 +75,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
// Policy for hide file extension setting
|
||||
_hideFileExtensionGpoRuleConfiguration = GPOWrapper.GetConfiguredNewPlusHideTemplateFilenameExtensionValue();
|
||||
_hideFileExtensionIsGPOConfigured = _hideFileExtensionGpoRuleConfiguration == GpoRuleConfigured.Disabled || _hideFileExtensionGpoRuleConfiguration == GpoRuleConfigured.Enabled;
|
||||
|
||||
// Same for Replace Variables
|
||||
_replaceVariablesIsGPOConfigured = GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Enabled
|
||||
|| GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Disabled;
|
||||
}
|
||||
|
||||
public bool IsEnabled
|
||||
@@ -92,6 +94,8 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
OnPropertyChanged(nameof(IsEnabled));
|
||||
OnPropertyChanged(nameof(IsHideFileExtSettingsCardEnabled));
|
||||
OnPropertyChanged(nameof(IsHideFileExtSettingGPOConfigured));
|
||||
OnPropertyChanged(nameof(IsReplaceVariablesSettingGPOConfigured));
|
||||
OnPropertyChanged(nameof(IsReplaceVariablesSettingsCardEnabled));
|
||||
|
||||
OutGoingGeneralSettings outgoingMessage = new OutGoingGeneralSettings(GeneralSettingsConfig);
|
||||
SendConfigMSG(outgoingMessage.ToString());
|
||||
@@ -156,6 +160,10 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
|
||||
public bool IsHideFileExtSettingGPOConfigured => _isNewPlusEnabled && _hideFileExtensionIsGPOConfigured;
|
||||
|
||||
public bool IsReplaceVariablesSettingsCardEnabled => _isNewPlusEnabled && !_replaceVariablesIsGPOConfigured;
|
||||
|
||||
public bool IsReplaceVariablesSettingGPOConfigured => _isNewPlusEnabled && _replaceVariablesIsGPOConfigured;
|
||||
|
||||
public bool HideStartingDigits
|
||||
{
|
||||
get => _hideStartingDigits;
|
||||
@@ -172,6 +180,32 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public bool ReplaceVariables
|
||||
{
|
||||
get
|
||||
{
|
||||
// Check to see if setting has been enabled or disabled via GPO, and if so, use that value
|
||||
if (IsReplaceVariablesSettingGPOConfigured)
|
||||
{
|
||||
return GPOWrapper.GetConfiguredNewPlusReplaceVariablesValue() == GpoRuleConfigured.Enabled;
|
||||
}
|
||||
|
||||
return _replaceVariables;
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
if (_replaceVariables != value)
|
||||
{
|
||||
_replaceVariables = value;
|
||||
Settings.Properties.ReplaceVariables.Value = value;
|
||||
OnPropertyChanged(nameof(ReplaceVariables));
|
||||
|
||||
NotifySettingsChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEnabledGpoConfigured
|
||||
{
|
||||
get => _enabledStateIsGPOConfigured;
|
||||
@@ -236,11 +270,13 @@ namespace Microsoft.PowerToys.Settings.UI.ViewModels
|
||||
private string _templateLocation;
|
||||
private bool _hideFileExtension;
|
||||
private bool _hideStartingDigits;
|
||||
private bool _replaceVariables;
|
||||
|
||||
private GpoRuleConfigured _enabledGpoRuleConfiguration;
|
||||
private bool _enabledStateIsGPOConfigured;
|
||||
private GpoRuleConfigured _hideFileExtensionGpoRuleConfiguration;
|
||||
private bool _hideFileExtensionIsGPOConfigured;
|
||||
private bool _replaceVariablesIsGPOConfigured;
|
||||
|
||||
public void RefreshEnabledState()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user