mirror of
https://github.com/microsoft/PowerToys.git
synced 2026-04-03 01:36:31 +02:00
New+: Fixed issue with files and folders containing only numbers (#45439)
## Summary of the Pull Request Supersedes https://github.com/microsoft/PowerToys/pull/41465 1) Fix for where template file or folder only contained numbers 2) Fix for where hidden files are shown in the list of templates ## PR Checklist - [x] Closes: #36216 - [x] **Communication:** I've discussed this with core contributors already. If the work hasn't been agreed, this work might be rejected - [n/a] **Tests:** Added/updated and all pass - [n/a] **Localization:** All end-user-facing strings can be localized - [n/a] **Dev docs:** Added/updated - [n/a] **New binaries:** Added on the required places - [n/a] [JSON for signing](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ESRPSigning_core.json) for new binaries - [n/a] [WXS for installer](https://github.com/microsoft/PowerToys/blob/main/installer/PowerToysSetup/Product.wxs) for new binaries and localization folder - [n/a] [YML for CI pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/ci/templates/build-powertoys-steps.yml) for new test projects - [n/a] [YML for signed pipeline](https://github.com/microsoft/PowerToys/blob/main/.pipelines/release.yml) - [n/a] **Documentation updated:** If checked, please file a pull request on [our docs repo](https://github.com/MicrosoftDocs/windows-uwp/tree/docs/hub/powertoys) and link it here: #xxx ## Detailed Description of the Pull Request / Additional comments 1) Fix for where template file or folder only contained numbers // Filename cases to support // type | filename | result // [file] | 01. First entry.txt | First entry.txt // [folder] | 02. Second entry | Second entry // [folder] | 03 Third entry | Third entry // [file] | 04 Fourth entry.txt | Fourth entry.txt // [file] | 05.Fifth entry.txt | Fifth entry.txt // [folder] | 001231 | 001231 // [file] | 001231.txt | 001231.txt // [file] | 13. 0123456789012345.txt | 0123456789012345.txt 2) Fix for where hidden files are shown in the list of templates Instead of excluding based on filename (desktop.ini) exclude based on hidden and system attribute being set ## Validation Steps Performed ### Before fix Notice 1) Folders with numbers only aren't displayed on the context menu 2) Files with extension with numbers only show extension on the context menu 3) Some hidden files are shown <img width="1893" height="786" alt="image" src="https://github.com/user-attachments/assets/3845a541-499f-47a7-ae99-a92886f74214" /> ### After fixes #### Scenario 1 New+ Setting: Hide leading digits…: Yes New+ Setting: Hide file extension: Yes New+ Setting: Replace variables: No <img width="1816" height="1185" alt="image" src="https://github.com/user-attachments/assets/5ed2c205-d5ce-4366-90d9-c08ef4d2881f" /> #### Scenario 2 New+ Setting: Hide leading digits…: No New+ Setting: Hide file extension: No New+ Setting: Replace variables: No <img width="1819" height="1197" alt="image" src="https://github.com/user-attachments/assets/710265d5-94e9-4fee-9a47-a7bbb78b45bd" /> #### Scenario 3 New+ Setting: Hide leading digits…: Yes New+ Setting: Hide file extension: Yes New+ Setting: Replace variables: Yes <img width="1816" height="1197" alt="image" src="https://github.com/user-attachments/assets/45a90cdd-ec21-4425-9de0-c323ec90f149" />
This commit is contained in:
committed by
GitHub
parent
9e4bf1e3e0
commit
7e3f9f0c3f
@@ -4,22 +4,6 @@
|
|||||||
|
|
||||||
namespace newplus::helpers::filesystem
|
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)
|
inline bool is_directory(const std::filesystem::path path)
|
||||||
{
|
{
|
||||||
const auto entry = std::filesystem::directory_entry(path);
|
const auto entry = std::filesystem::directory_entry(path);
|
||||||
|
|||||||
@@ -129,6 +129,18 @@ namespace newplus::helpers::variables
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool exclude_item(const std::filesystem::path& path)
|
||||||
|
{
|
||||||
|
DWORD attrs = GetFileAttributesW(path.c_str());
|
||||||
|
if (attrs == INVALID_FILE_ATTRIBUTES)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exclude if hidden or system
|
||||||
|
return (attrs & (FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
inline void resolve_variables_in_filename_and_rename_files(const std::filesystem::path& path, const bool do_rename = true)
|
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
|
// Depth first recursion, so that we start renaming the leaves, and avoid having to rescan
|
||||||
@@ -143,7 +155,7 @@ namespace newplus::helpers::variables
|
|||||||
// Perform the actual rename
|
// Perform the actual rename
|
||||||
for (const auto& current : std::filesystem::directory_iterator(path))
|
for (const auto& current : std::filesystem::directory_iterator(path))
|
||||||
{
|
{
|
||||||
if (!newplus::helpers::filesystem::is_hidden(current))
|
if (!exclude_item(current))
|
||||||
{
|
{
|
||||||
const std::filesystem::path resolved_path = resolve_variables_in_path(current.path());
|
const std::filesystem::path resolved_path = resolve_variables_in_path(current.path());
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ void template_folder::rescan_template_folder()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!helpers::filesystem::is_hidden(entry.path()))
|
if (!newplus::helpers::variables::exclude_item(entry.path()))
|
||||||
{
|
{
|
||||||
files.push_back({ entry.path().wstring(), new template_item(entry) });
|
files.push_back({ entry.path().wstring(), new template_item(entry) });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,3 @@
|
|||||||
|
|
||||||
|
|
||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "template_item.h"
|
#include "template_item.h"
|
||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
@@ -60,10 +58,91 @@ 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
|
std::wstring template_item::remove_starting_digits_from_filename(std::wstring filename) const
|
||||||
{
|
{
|
||||||
filename.erase(0, std::min(filename.find_first_not_of(L"0123456789"), filename.size()));
|
// Filename cases to support
|
||||||
filename.erase(0, std::min(filename.find_first_not_of(L" ."), filename.size()));
|
// type | filename | result
|
||||||
|
// [file] | 01. First entry.txt | First entry.txt
|
||||||
|
// [folder] | 02. Second entry | Second entry
|
||||||
|
// [folder] | 03 Third entry | Third entry
|
||||||
|
// [file] | 04 Fourth entry.txt | Fourth entry.txt
|
||||||
|
// [file] | 05.Fifth entry.txt | Fifth entry.txt
|
||||||
|
// [folder] | 001231 | 001231
|
||||||
|
// [file] | 001231.txt | 001231.txt
|
||||||
|
// [file] | 13. 0123456789012345.txt | 0123456789012345.txt
|
||||||
|
|
||||||
return filename;
|
std::filesystem::path filename_path(filename);
|
||||||
|
const std::wstring stem = filename_path.stem().wstring();
|
||||||
|
|
||||||
|
bool stem_is_only_digits = !stem.empty();
|
||||||
|
for (const wchar_t c : stem)
|
||||||
|
{
|
||||||
|
if (c < L'0' || c > L'9')
|
||||||
|
{
|
||||||
|
stem_is_only_digits = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stem_is_only_digits)
|
||||||
|
{
|
||||||
|
// Edge cases where digits ARE the filename.
|
||||||
|
// If it's a file, we always keep it (e.g. 001231.txt or 001231).
|
||||||
|
// If it's a folder, we only strip if it looks like it has an extension (which is actually part of the name for folders).
|
||||||
|
// e.g. "0123.Name" -> Strip. "001231" -> Keep.
|
||||||
|
const bool is_folder = helpers::filesystem::is_directory(path);
|
||||||
|
const bool has_extension = filename_path.has_extension();
|
||||||
|
|
||||||
|
if (!is_folder || !has_extension)
|
||||||
|
{
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find end of leading digits
|
||||||
|
size_t digits_end_index = 0;
|
||||||
|
while (digits_end_index < filename.length() && filename[digits_end_index] >= L'0' && filename[digits_end_index] <= L'9')
|
||||||
|
{
|
||||||
|
digits_end_index++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (digits_end_index == 0)
|
||||||
|
{
|
||||||
|
// No leading digits
|
||||||
|
return filename;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Determine if we should also strip a separator (dot or space)
|
||||||
|
size_t strip_length = digits_end_index;
|
||||||
|
|
||||||
|
// Check patterns to strip separators:
|
||||||
|
// 1. "01. Name" -> Strip "01. "
|
||||||
|
// 2. "01 .Name" -> Strip "01 ."
|
||||||
|
// 3. "01.Name" -> Strip "01."
|
||||||
|
// 4. "01 Name" -> Strip "01 "
|
||||||
|
// 5. "01Name" -> Strip "01" (No separator)
|
||||||
|
|
||||||
|
if (strip_length < filename.length())
|
||||||
|
{
|
||||||
|
if (filename[strip_length] == L'.')
|
||||||
|
{
|
||||||
|
strip_length++;
|
||||||
|
// If dot is followed by space, strip that too (e.g. "01. Name")
|
||||||
|
if (strip_length < filename.length() && filename[strip_length] == L' ')
|
||||||
|
{
|
||||||
|
strip_length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filename[strip_length] == L' ')
|
||||||
|
{
|
||||||
|
strip_length++;
|
||||||
|
// If space is followed by dot, strip that too (e.g. "01 .Name")
|
||||||
|
if (strip_length < filename.length() && filename[strip_length] == L'.')
|
||||||
|
{
|
||||||
|
strip_length++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return filename.substr(strip_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::wstring template_item::get_explorer_icon() const
|
std::wstring template_item::get_explorer_icon() const
|
||||||
|
|||||||
Reference in New Issue
Block a user