mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[PowerRename] Using File Date Time Attributes Feature (#4722)
* Add basic using file attributes functionality * Correctly return result * Refactor * Move retrieving date attribute to get function * Cover various milliseconds patterns, retrieve file attributes only when needed * Correctly check if date/time pattern is used. Remove wstring cast * Use correct flags on CreateFile call to handle directories * rebase to master * Perform transform operation at last to make it not mess with date/time variables * Refactor, remove extra space
This commit is contained in:
committed by
GitHub
parent
ebe70a52d1
commit
a8153dd8db
@@ -1,5 +1,6 @@
|
|||||||
#include "pch.h"
|
#include "pch.h"
|
||||||
#include "Helpers.h"
|
#include "Helpers.h"
|
||||||
|
#include <regex>
|
||||||
#include <ShlGuid.h>
|
#include <ShlGuid.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
@@ -168,6 +169,63 @@ HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR sour
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME LocalTime)
|
||||||
|
{
|
||||||
|
HRESULT hr = (source && wcslen(source) > 0) ? S_OK : E_INVALIDARG;
|
||||||
|
if (SUCCEEDED(hr))
|
||||||
|
{
|
||||||
|
std::wregex pattern(L"\\$YYYY");
|
||||||
|
std::wstring res(source);
|
||||||
|
wchar_t replaceTerm[MAX_PATH] = {0};
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%d"),LocalTime.wYear);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$SSS";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%03d"), LocalTime.wMilliseconds);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$MMM";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%03d"), LocalTime.wMilliseconds);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$mmm";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%03d"), LocalTime.wMilliseconds);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$fff";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%03d"), LocalTime.wMilliseconds);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$FFF";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%03d"), LocalTime.wMilliseconds);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$MM" ;
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%02d"), LocalTime.wMonth);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$DD";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%02d"), LocalTime.wDay);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$hh";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%02d"), LocalTime.wHour);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$mm";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%02d"), LocalTime.wMinute);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
pattern = L"\\$ss";
|
||||||
|
StringCchPrintf(replaceTerm, MAX_PATH, TEXT("%02d"), LocalTime.wSecond);
|
||||||
|
res = regex_replace(res, pattern, replaceTerm);
|
||||||
|
|
||||||
|
hr = StringCchCopy(result, cchMax, res.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ IPowerRenameManager* psrm, _In_ int depth = 0)
|
HRESULT _ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ IPowerRenameManager* psrm, _In_ int depth = 0)
|
||||||
{
|
{
|
||||||
HRESULT hr = E_INVALIDARG;
|
HRESULT hr = E_INVALIDARG;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source);
|
HRESULT GetTrimmedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source);
|
||||||
HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags);
|
HRESULT GetTransformedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, DWORD flags);
|
||||||
|
HRESULT GetDatedFileName(_Out_ PWSTR result, UINT cchMax, _In_ PCWSTR source, SYSTEMTIME LocalTime);
|
||||||
HRESULT EnumerateDataObject(_In_ IUnknown* pdo, _In_ IPowerRenameManager* psrm);
|
HRESULT EnumerateDataObject(_In_ IUnknown* pdo, _In_ IPowerRenameManager* psrm);
|
||||||
BOOL GetEnumeratedFileName(
|
BOOL GetEnumeratedFileName(
|
||||||
__out_ecount(cchMax) PWSTR pszUniqueName,
|
__out_ecount(cchMax) PWSTR pszUniqueName,
|
||||||
@@ -12,4 +13,4 @@ BOOL GetEnumeratedFileName(
|
|||||||
__in PCWSTR pszTemplate,
|
__in PCWSTR pszTemplate,
|
||||||
__in_opt PCWSTR pszDir,
|
__in_opt PCWSTR pszDir,
|
||||||
unsigned long ulMinLong,
|
unsigned long ulMinLong,
|
||||||
__inout unsigned long* pulNumUsed);
|
__inout unsigned long* pulNumUsed);
|
||||||
|
|||||||
@@ -43,6 +43,7 @@ interface __declspec(uuid("C7F59201-4DE1-4855-A3A2-26FC3279C8A5")) IPowerRenameI
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IFACEMETHOD(get_path)(_Outptr_ PWSTR* path) = 0;
|
IFACEMETHOD(get_path)(_Outptr_ PWSTR* path) = 0;
|
||||||
|
IFACEMETHOD(get_date)(_Outptr_ SYSTEMTIME* date) = 0;
|
||||||
IFACEMETHOD(get_shellItem)(_Outptr_ IShellItem** ppsi) = 0;
|
IFACEMETHOD(get_shellItem)(_Outptr_ IShellItem** ppsi) = 0;
|
||||||
IFACEMETHOD(get_originalName)(_Outptr_ PWSTR* originalName) = 0;
|
IFACEMETHOD(get_originalName)(_Outptr_ PWSTR* originalName) = 0;
|
||||||
IFACEMETHOD(get_newName)(_Outptr_ PWSTR* newName) = 0;
|
IFACEMETHOD(get_newName)(_Outptr_ PWSTR* newName) = 0;
|
||||||
|
|||||||
@@ -42,6 +42,36 @@ IFACEMETHODIMP CPowerRenameItem::get_path(_Outptr_ PWSTR* path)
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IFACEMETHODIMP CPowerRenameItem::get_date(_Outptr_ SYSTEMTIME* date)
|
||||||
|
{
|
||||||
|
CSRWSharedAutoLock lock(&m_lock);
|
||||||
|
HRESULT hr = m_isDateParsed ? S_OK : E_FAIL ;
|
||||||
|
if (!m_isDateParsed)
|
||||||
|
{
|
||||||
|
HANDLE hFile = CreateFileW(m_path, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
|
||||||
|
if (hFile != INVALID_HANDLE_VALUE)
|
||||||
|
{
|
||||||
|
FILETIME CreationTime;
|
||||||
|
if (GetFileTime(hFile, &CreationTime, NULL, NULL))
|
||||||
|
{
|
||||||
|
SYSTEMTIME SystemTime, LocalTime;
|
||||||
|
if (FileTimeToSystemTime(&CreationTime, &SystemTime))
|
||||||
|
{
|
||||||
|
if (SystemTimeToTzSpecificLocalTime(NULL, &SystemTime, &LocalTime))
|
||||||
|
{
|
||||||
|
m_date = LocalTime;
|
||||||
|
m_isDateParsed = true;
|
||||||
|
hr = S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
*date = m_date;
|
||||||
|
return hr;
|
||||||
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP CPowerRenameItem::get_shellItem(_Outptr_ IShellItem** ppsi)
|
IFACEMETHODIMP CPowerRenameItem::get_shellItem(_Outptr_ IShellItem** ppsi)
|
||||||
{
|
{
|
||||||
return SHCreateItemFromParsingName(m_path, nullptr, IID_PPV_ARGS(ppsi));
|
return SHCreateItemFromParsingName(m_path, nullptr, IID_PPV_ARGS(ppsi));
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ public:
|
|||||||
|
|
||||||
// IPowerRenameItem
|
// IPowerRenameItem
|
||||||
IFACEMETHODIMP get_path(_Outptr_ PWSTR* path);
|
IFACEMETHODIMP get_path(_Outptr_ PWSTR* path);
|
||||||
|
IFACEMETHODIMP get_date(_Outptr_ SYSTEMTIME* date);
|
||||||
IFACEMETHODIMP get_shellItem(_Outptr_ IShellItem** ppsi);
|
IFACEMETHODIMP get_shellItem(_Outptr_ IShellItem** ppsi);
|
||||||
IFACEMETHODIMP get_originalName(_Outptr_ PWSTR* originalName);
|
IFACEMETHODIMP get_originalName(_Outptr_ PWSTR* originalName);
|
||||||
IFACEMETHODIMP put_newName(_In_opt_ PCWSTR newName);
|
IFACEMETHODIMP put_newName(_In_opt_ PCWSTR newName);
|
||||||
@@ -46,15 +47,17 @@ protected:
|
|||||||
|
|
||||||
HRESULT _Init(_In_ IShellItem* psi);
|
HRESULT _Init(_In_ IShellItem* psi);
|
||||||
|
|
||||||
bool m_selected = true;
|
bool m_selected = true;
|
||||||
bool m_isFolder = false;
|
bool m_isFolder = false;
|
||||||
int m_id = -1;
|
bool m_isDateParsed = false;
|
||||||
int m_iconIndex = -1;
|
int m_id = -1;
|
||||||
UINT m_depth = 0;
|
int m_iconIndex = -1;
|
||||||
HRESULT m_error = S_OK;
|
UINT m_depth = 0;
|
||||||
PWSTR m_path = nullptr;
|
HRESULT m_error = S_OK;
|
||||||
PWSTR m_originalName = nullptr;
|
PWSTR m_path = nullptr;
|
||||||
PWSTR m_newName = nullptr;
|
PWSTR m_originalName = nullptr;
|
||||||
CSRWLock m_lock;
|
PWSTR m_newName = nullptr;
|
||||||
long m_refCount = 0;
|
SYSTEMTIME m_date;
|
||||||
};
|
CSRWLock m_lock;
|
||||||
|
long m_refCount = 0;
|
||||||
|
};
|
||||||
|
|||||||
@@ -813,7 +813,35 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
|
|||||||
newNameToUse = trimmedName;
|
newNameToUse = trimmedName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool isDateAttributeUsed = false;
|
||||||
|
wchar_t datedName[MAX_PATH] = { 0 };
|
||||||
|
std::wstring patterns[] = { L"$YYYY", L"$SSS", L"$MMM", L"$mmm", L"$FFF", L"$fff",
|
||||||
|
L"$MM", L"$DD", L"$hh", L"$mm", L"$ss" };
|
||||||
|
size_t patternsLength = ARRAYSIZE(patterns);
|
||||||
|
SYSTEMTIME LocalTime;
|
||||||
|
|
||||||
|
if (newNameToUse != nullptr)
|
||||||
|
{
|
||||||
|
for (size_t i = 0; !isDateAttributeUsed && i < patternsLength; i++)
|
||||||
|
{
|
||||||
|
std::wstring source(newNameToUse);
|
||||||
|
if (source.find(patterns[i]) != std::string::npos)
|
||||||
|
{
|
||||||
|
isDateAttributeUsed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (isDateAttributeUsed)
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(spItem->get_date(&LocalTime)))
|
||||||
|
{
|
||||||
|
if (SUCCEEDED(GetDatedFileName(datedName, ARRAYSIZE(datedName), newNameToUse, LocalTime)))
|
||||||
|
{
|
||||||
|
newNameToUse = datedName;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
wchar_t transformedName[MAX_PATH] = { 0 };
|
wchar_t transformedName[MAX_PATH] = { 0 };
|
||||||
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
|
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase))
|
||||||
{
|
{
|
||||||
@@ -822,7 +850,7 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
|
|||||||
newNameToUse = transformedName;
|
newNameToUse = transformedName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// No change from originalName so set newName to
|
// No change from originalName so set newName to
|
||||||
// null so we clear it from our UI as well.
|
// null so we clear it from our UI as well.
|
||||||
if (lstrcmp(originalName, newNameToUse) == 0)
|
if (lstrcmp(originalName, newNameToUse) == 0)
|
||||||
|
|||||||
Reference in New Issue
Block a user