mirror of
https://github.com/microsoft/PowerToys.git
synced 2025-12-16 11:48:06 +01:00
[PowerRename] Handle many items w/o crashing and OOM (#26761)
* [PowerRename] Disable AnimatedIcon (check mark) for CheckBox to prevent crashes * [PowerRename] Implement lightweight ExplorerItemsSource/VM
This commit is contained in:
@@ -1060,7 +1060,36 @@
|
|||||||
</Setter.Value>
|
</Setter.Value>
|
||||||
</Setter>
|
</Setter>
|
||||||
</Style>
|
</Style>
|
||||||
|
<!-- Copied from https://github.com/microsoft/microsoft-ui-xaml/blob/210349d9d1e0aeb8ee144ec027071a68ac578bdb/dev/CommonStyles/CheckBox_themeresources.xaml#L794 to remove the AnimatedIcon that caused crashes (see #18478). -->
|
||||||
|
<Style x:Key="CheckBoxDefaultStyleOverride" TargetType="CheckBox">
|
||||||
|
<Setter Property="Background"
|
||||||
|
Value="{ThemeResource CheckBoxBackgroundUnchecked}" />
|
||||||
|
<Setter Property="Foreground"
|
||||||
|
Value="{ThemeResource CheckBoxForegroundUnchecked}" />
|
||||||
|
<Setter Property="BorderBrush"
|
||||||
|
Value="{ThemeResource CheckBoxBorderBrushUnchecked}" />
|
||||||
|
<Setter Property="Padding"
|
||||||
|
Value="{StaticResource CheckBoxPadding}" />
|
||||||
|
<Setter Property="HorizontalAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalAlignment" Value="Center" />
|
||||||
|
<Setter Property="HorizontalContentAlignment" Value="Left" />
|
||||||
|
<Setter Property="VerticalContentAlignment" Value="Top" />
|
||||||
|
<Setter Property="FontFamily"
|
||||||
|
Value="{ThemeResource ContentControlThemeFontFamily}" />
|
||||||
|
<Setter Property="FontSize"
|
||||||
|
Value="{ThemeResource ControlContentThemeFontSize}" />
|
||||||
|
<Setter Property="MinWidth"
|
||||||
|
Value="{StaticResource CheckBoxMinWidth}" />
|
||||||
|
<Setter Property="MinHeight"
|
||||||
|
Value="{StaticResource CheckBoxHeight}" />
|
||||||
|
<Setter Property="UseSystemFocusVisuals"
|
||||||
|
Value="{StaticResource UseSystemFocusVisuals}" />
|
||||||
|
<Setter Property="FocusVisualMargin"
|
||||||
|
Value="{StaticResource CheckBoxFocusVisualMargin}" />
|
||||||
|
<Setter Property="CornerRadius"
|
||||||
|
Value="{ThemeResource ControlCornerRadius}" />
|
||||||
|
|
||||||
|
</Style>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
</Application.Resources>
|
</Application.Resources>
|
||||||
</Application>
|
</Application>
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include <common/logger/logger.h>
|
#include <common/logger/logger.h>
|
||||||
#include <common/logger/logger_settings.h>
|
#include <common/logger/logger_settings.h>
|
||||||
@@ -18,8 +19,9 @@ using namespace winrt::Microsoft::UI::Xaml::Navigation;
|
|||||||
using namespace PowerRenameUI;
|
using namespace PowerRenameUI;
|
||||||
using namespace PowerRenameUI::implementation;
|
using namespace PowerRenameUI::implementation;
|
||||||
|
|
||||||
// To learn more about WinUI, the WinUI project structure,
|
namespace fs = std::filesystem;
|
||||||
// and more about our project templates, see: http://aka.ms/winui-project-info.
|
|
||||||
|
//#define DEBUG_BENCHMARK_100K_ENTRIES
|
||||||
|
|
||||||
std::vector<std::wstring> g_files;
|
std::vector<std::wstring> g_files;
|
||||||
|
|
||||||
@@ -34,8 +36,7 @@ App::App()
|
|||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
|
#if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION
|
||||||
UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e)
|
UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) {
|
||||||
{
|
|
||||||
if (IsDebuggerPresent())
|
if (IsDebuggerPresent())
|
||||||
{
|
{
|
||||||
auto errorMessage = e.Message();
|
auto errorMessage = e.Message();
|
||||||
@@ -112,6 +113,50 @@ void App::OnLaunched(LaunchActivatedEventArgs const&)
|
|||||||
ExitProcess(1);
|
ExitProcess(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef DEBUG_BENCHMARK_100K_ENTRIES
|
||||||
|
const std::wstring_view ROOT_PATH = L"R:\\PowerRenameBenchmark";
|
||||||
|
|
||||||
|
std::wstring subdirectory_name = L"0";
|
||||||
|
std::error_code _;
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
constexpr bool recreate_files = true;
|
||||||
|
#else
|
||||||
|
constexpr bool recreate_files = false;
|
||||||
|
#endif
|
||||||
|
if constexpr (recreate_files)
|
||||||
|
fs::remove_all(ROOT_PATH, _);
|
||||||
|
|
||||||
|
g_files.push_back(fs::path{ ROOT_PATH });
|
||||||
|
constexpr int pow2_threshold = 10;
|
||||||
|
constexpr int num_files = 100'000;
|
||||||
|
for (int i = 0; i < num_files; ++i)
|
||||||
|
{
|
||||||
|
fs::path file_path{ ROOT_PATH };
|
||||||
|
// Create a subdirectory for each subsequent 2^pow2_threshold files, o/w filesystem becomes too slow to create them in a reasonable time.
|
||||||
|
if ((i & ((1 << pow2_threshold) - 1)) == 0)
|
||||||
|
{
|
||||||
|
subdirectory_name = std::to_wstring(i >> pow2_threshold);
|
||||||
|
}
|
||||||
|
|
||||||
|
file_path /= subdirectory_name;
|
||||||
|
|
||||||
|
if constexpr (recreate_files)
|
||||||
|
{
|
||||||
|
fs::create_directories(file_path, _);
|
||||||
|
file_path /= std::to_wstring(i) + L".txt";
|
||||||
|
HANDLE hFile = CreateFileW(
|
||||||
|
file_path.c_str(),
|
||||||
|
GENERIC_WRITE,
|
||||||
|
0,
|
||||||
|
nullptr,
|
||||||
|
CREATE_NEW,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
nullptr);
|
||||||
|
CloseHandle(hFile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
#define BUFSIZE 4096 * 4
|
#define BUFSIZE 4096 * 4
|
||||||
|
|
||||||
BOOL bSuccess;
|
BOOL bSuccess;
|
||||||
@@ -139,6 +184,7 @@ void App::OnLaunched(LaunchActivatedEventArgs const&)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
CloseHandle(hStdin);
|
CloseHandle(hStdin);
|
||||||
|
#endif
|
||||||
|
|
||||||
Logger::debug(L"Starting PowerRename with {} files selected", g_files.size());
|
Logger::debug(L"Starting PowerRename with {} files selected", g_files.size());
|
||||||
|
|
||||||
|
|||||||
@@ -41,10 +41,33 @@ namespace
|
|||||||
|
|
||||||
namespace winrt::PowerRenameUI::implementation
|
namespace winrt::PowerRenameUI::implementation
|
||||||
{
|
{
|
||||||
ExplorerItem::ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked) :
|
DependencyProperty ExplorerItem::_CheckedProperty{ nullptr };
|
||||||
m_id{ id }, m_idStr{ std::to_wstring(id) }, m_original{ original }, m_renamed{ renamed }, m_type{ type }, m_depth{ depth }, m_checked{ checked }
|
|
||||||
|
void ExplorerItem::_InitializeProperties()
|
||||||
{
|
{
|
||||||
|
if (!_CheckedProperty)
|
||||||
|
{
|
||||||
|
_CheckedProperty =
|
||||||
|
DependencyProperty::Register(
|
||||||
|
L"Checked",
|
||||||
|
xaml_typename<bool>(),
|
||||||
|
xaml_typename<PowerRenameUI::ExplorerItem>(),
|
||||||
|
PropertyMetadata{ box_value(true) });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerItem::ExplorerItem()
|
||||||
|
{
|
||||||
|
_InitializeProperties();
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerItem::ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked) :
|
||||||
|
m_id{ id }, m_idStr{ std::to_wstring(id) }, m_original{ original }, m_renamed{ renamed }, m_type{ type }, m_depth{ depth }
|
||||||
|
{
|
||||||
|
_InitializeProperties();
|
||||||
|
|
||||||
m_imagePath = (m_type == static_cast<UINT>(ExplorerItemType::Folder)) ? folderImagePath : fileImagePath;
|
m_imagePath = (m_type == static_cast<UINT>(ExplorerItemType::Folder)) ? folderImagePath : fileImagePath;
|
||||||
|
Checked(checked);
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ExplorerItem::Id()
|
int32_t ExplorerItem::Id()
|
||||||
@@ -52,11 +75,29 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
return m_id;
|
return m_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExplorerItem::Id(const int32_t value)
|
||||||
|
{
|
||||||
|
if (m_id != value)
|
||||||
|
{
|
||||||
|
m_id = value;
|
||||||
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Id" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hstring ExplorerItem::IdStr()
|
hstring ExplorerItem::IdStr()
|
||||||
{
|
{
|
||||||
return m_idStr;
|
return m_idStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExplorerItem::IdStr(hstring const& value)
|
||||||
|
{
|
||||||
|
if (m_idStr != value)
|
||||||
|
{
|
||||||
|
m_idStr = value;
|
||||||
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"IdStr" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hstring ExplorerItem::Original()
|
hstring ExplorerItem::Original()
|
||||||
{
|
{
|
||||||
return m_original;
|
return m_original;
|
||||||
@@ -90,11 +131,29 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
return static_cast<double>(m_depth) * 12;
|
return static_cast<double>(m_depth) * 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExplorerItem::Indentation(double value)
|
||||||
|
{
|
||||||
|
if (m_depth != value)
|
||||||
|
{
|
||||||
|
m_depth = static_cast<uint32_t>(value / 12);
|
||||||
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Indentation" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
hstring ExplorerItem::ImagePath()
|
hstring ExplorerItem::ImagePath()
|
||||||
{
|
{
|
||||||
return m_imagePath;
|
return m_imagePath;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ExplorerItem::ImagePath(hstring const& value)
|
||||||
|
{
|
||||||
|
if (m_imagePath != value)
|
||||||
|
{
|
||||||
|
m_imagePath = value;
|
||||||
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"ImagePath" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
int32_t ExplorerItem::Type()
|
int32_t ExplorerItem::Type()
|
||||||
{
|
{
|
||||||
return m_type;
|
return m_type;
|
||||||
@@ -109,29 +168,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ExplorerItem::Checked()
|
|
||||||
{
|
|
||||||
return m_checked;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ExplorerItem::Checked(bool value)
|
|
||||||
{
|
|
||||||
if (m_checked != value)
|
|
||||||
{
|
|
||||||
m_checked = value;
|
|
||||||
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"Checked" });
|
|
||||||
|
|
||||||
if (m_checked && !m_renamed.empty())
|
|
||||||
{
|
|
||||||
VisualStateManager::GoToState(*this, PowerRenameItemRenameStatusToString(m_state), false);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
VisualStateManager::GoToState(*this, L"Normal", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
int32_t ExplorerItem::State()
|
int32_t ExplorerItem::State()
|
||||||
{
|
{
|
||||||
return static_cast<int32_t>(m_state);
|
return static_cast<int32_t>(m_state);
|
||||||
@@ -166,7 +202,7 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
{
|
{
|
||||||
static auto factory = winrt::get_activation_factory<ResourceManager, IResourceManagerFactory>();
|
static auto factory = winrt::get_activation_factory<ResourceManager, IResourceManagerFactory>();
|
||||||
static ResourceManager manager = factory.CreateInstance(L"resources.pri");
|
static ResourceManager manager = factory.CreateInstance(L"resources.pri");
|
||||||
static auto invalid_char_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString();
|
static auto invalid_char_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_InvalidChar").ValueAsString();
|
||||||
static auto name_too_long_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_FileNameTooLong").ValueAsString();
|
static auto name_too_long_error = manager.MainResourceMap().GetValue(L"Resources/ErrorMessage_FileNameTooLong").ValueAsString();
|
||||||
|
|
||||||
switch (m_state)
|
switch (m_state)
|
||||||
@@ -182,7 +218,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
default:
|
default:
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "winrt/Microsoft.UI.Xaml.Controls.Primitives.h"
|
#include "winrt/Microsoft.UI.Xaml.Controls.Primitives.h"
|
||||||
#include "ExplorerItem.g.h"
|
#include "ExplorerItem.g.h"
|
||||||
#include "PowerRenameInterfaces.h"
|
#include "PowerRenameInterfaces.h"
|
||||||
|
#include "Utils.h"
|
||||||
|
|
||||||
namespace winrt::PowerRenameUI::implementation
|
namespace winrt::PowerRenameUI::implementation
|
||||||
{
|
{
|
||||||
@@ -17,27 +18,39 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
File = 1
|
File = 1
|
||||||
};
|
};
|
||||||
|
|
||||||
ExplorerItem() = default;
|
ExplorerItem();
|
||||||
|
|
||||||
ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked);
|
ExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked);
|
||||||
int32_t Id();
|
int32_t Id();
|
||||||
|
void Id(int32_t);
|
||||||
|
|
||||||
hstring IdStr();
|
hstring IdStr();
|
||||||
|
void IdStr(hstring const& value);
|
||||||
|
|
||||||
hstring Original();
|
hstring Original();
|
||||||
void Original(hstring const& value);
|
void Original(hstring const& value);
|
||||||
|
|
||||||
hstring Renamed();
|
hstring Renamed();
|
||||||
void Renamed(hstring const& value);
|
void Renamed(hstring const& value);
|
||||||
|
|
||||||
double Indentation();
|
double Indentation();
|
||||||
|
void Indentation(double value);
|
||||||
|
|
||||||
hstring ImagePath();
|
hstring ImagePath();
|
||||||
|
void ImagePath(hstring const& value);
|
||||||
int32_t Type();
|
int32_t Type();
|
||||||
void Type(int32_t value);
|
void Type(int32_t value);
|
||||||
bool Checked();
|
|
||||||
void Checked(bool value);
|
|
||||||
int32_t State();
|
int32_t State();
|
||||||
void State(int32_t value);
|
void State(int32_t value);
|
||||||
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
|
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
|
||||||
void PropertyChanged(winrt::event_token const& token) noexcept;
|
void PropertyChanged(winrt::event_token const& token) noexcept;
|
||||||
|
|
||||||
|
DEPENDENCY_PROPERTY(bool, Checked);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
static void _InitializeProperties();
|
||||||
|
|
||||||
std::wstring StateToErrorMessage();
|
std::wstring StateToErrorMessage();
|
||||||
|
|
||||||
int32_t m_id{};
|
int32_t m_id{};
|
||||||
@@ -47,7 +60,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
uint32_t m_depth{};
|
uint32_t m_depth{};
|
||||||
hstring m_imagePath;
|
hstring m_imagePath;
|
||||||
int32_t m_type{};
|
int32_t m_type{};
|
||||||
bool m_checked{};
|
|
||||||
PowerRenameItemRenameStatus m_state{};
|
PowerRenameItemRenameStatus m_state{};
|
||||||
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,14 +4,17 @@ namespace PowerRenameUI
|
|||||||
{
|
{
|
||||||
ExplorerItem();
|
ExplorerItem();
|
||||||
ExplorerItem(Int32 id, String original, String renamed, Int32 type, UInt32 depth, Boolean checked);
|
ExplorerItem(Int32 id, String original, String renamed, Int32 type, UInt32 depth, Boolean checked);
|
||||||
Int32 Id { get; };
|
|
||||||
String IdStr { get; };
|
Int32 Id;
|
||||||
|
String IdStr;
|
||||||
String Original;
|
String Original;
|
||||||
String Renamed;
|
String Renamed;
|
||||||
Double Indentation { get; };
|
Double Indentation;
|
||||||
String ImagePath { get; };
|
String ImagePath;
|
||||||
Int32 Type;
|
Int32 Type;
|
||||||
Boolean Checked;
|
Boolean Checked;
|
||||||
Int32 State;
|
Int32 State;
|
||||||
|
|
||||||
|
static Microsoft.UI.Xaml.DependencyProperty CheckedProperty { get; };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -49,7 +49,9 @@
|
|||||||
IsChecked="{x:Bind Checked, Mode=TwoWay}"
|
IsChecked="{x:Bind Checked, Mode=TwoWay}"
|
||||||
IsTabStop="True"
|
IsTabStop="True"
|
||||||
TabIndex="0"
|
TabIndex="0"
|
||||||
|
Style="{StaticResource CheckBoxDefaultStyleOverride}"
|
||||||
XYFocusKeyboardNavigation="Enabled" />
|
XYFocusKeyboardNavigation="Enabled" />
|
||||||
|
|
||||||
<Image
|
<Image
|
||||||
Grid.Column="2"
|
Grid.Column="2"
|
||||||
Width="16"
|
Width="16"
|
||||||
|
|||||||
@@ -0,0 +1,123 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
#include "ExplorerItemViewModel.h"
|
||||||
|
#if __has_include("ExplorerItemViewModel.g.cpp")
|
||||||
|
#include "ExplorerItemViewModel.g.cpp"
|
||||||
|
#endif
|
||||||
|
#include <PowerRenameManager.h>
|
||||||
|
#include <Renaming.h>
|
||||||
|
|
||||||
|
extern CComPtr<IPowerRenameManager> g_prManager;
|
||||||
|
extern std::function<void(void)> g_itemToggledCallback;
|
||||||
|
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
const wchar_t fileImagePath[] = L"ms-appx:///Assets/file.png";
|
||||||
|
const wchar_t folderImagePath[] = L"ms-appx:///Assets/folder.png";
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::implementation
|
||||||
|
{
|
||||||
|
winrt::event_token ExplorerItemViewModel::PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
|
||||||
|
{
|
||||||
|
return m_propertyChanged.add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ExplorerItemViewModel::PropertyChanged(winrt::event_token const& token) noexcept
|
||||||
|
{
|
||||||
|
m_propertyChanged.remove(token);
|
||||||
|
}
|
||||||
|
ExplorerItemViewModel::ExplorerItemViewModel(const uint32_t _index) :
|
||||||
|
_index{ _index }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ExplorerItemViewModel::IdVM()
|
||||||
|
{
|
||||||
|
return _index;
|
||||||
|
}
|
||||||
|
hstring ExplorerItemViewModel::IdStrVM()
|
||||||
|
{
|
||||||
|
return to_hstring(_index);
|
||||||
|
}
|
||||||
|
hstring ExplorerItemViewModel::OriginalVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
|
||||||
|
PWSTR originalName = nullptr;
|
||||||
|
winrt::check_hresult(spItem->GetOriginalName(&originalName));
|
||||||
|
|
||||||
|
return hstring{ originalName };
|
||||||
|
}
|
||||||
|
hstring ExplorerItemViewModel::RenamedVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
|
||||||
|
PWSTR newName = nullptr;
|
||||||
|
spItem->GetNewName(&newName);
|
||||||
|
if (!newName)
|
||||||
|
{
|
||||||
|
return L"";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
hstring result{ newName };
|
||||||
|
SHFree(newName);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double ExplorerItemViewModel::IndentationVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
|
||||||
|
UINT depth = 0;
|
||||||
|
spItem->GetDepth(&depth);
|
||||||
|
|
||||||
|
return static_cast<double>(depth) * 12;
|
||||||
|
}
|
||||||
|
hstring ExplorerItemViewModel::ImagePathVM()
|
||||||
|
{
|
||||||
|
return TypeVM() ? fileImagePath : folderImagePath;
|
||||||
|
}
|
||||||
|
int32_t ExplorerItemViewModel::TypeVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
|
||||||
|
bool isFolder = false;
|
||||||
|
spItem->GetIsFolder(&isFolder);
|
||||||
|
return isFolder ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExplorerItemViewModel::CheckedVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
bool result = false;
|
||||||
|
winrt::check_hresult(spItem->GetSelected(&result));
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
void ExplorerItemViewModel::CheckedVM(bool value)
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
winrt::check_hresult(spItem->PutSelected(value));
|
||||||
|
g_itemToggledCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t ExplorerItemViewModel::StateVM()
|
||||||
|
{
|
||||||
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
|
winrt::check_hresult(g_prManager->GetItemByIndex(_index, &spItem));
|
||||||
|
PowerRenameItemRenameStatus status {};
|
||||||
|
spItem->GetStatus(&status);
|
||||||
|
return static_cast<int32_t>(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "ExplorerItemViewModel.g.h"
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::implementation
|
||||||
|
{
|
||||||
|
struct ExplorerItemViewModel : ExplorerItemViewModelT<ExplorerItemViewModel>
|
||||||
|
{
|
||||||
|
ExplorerItemViewModel() = default;
|
||||||
|
ExplorerItemViewModel(const uint32_t _index);
|
||||||
|
|
||||||
|
int32_t IdVM();
|
||||||
|
hstring IdStrVM();
|
||||||
|
hstring OriginalVM();
|
||||||
|
hstring RenamedVM();
|
||||||
|
double IndentationVM();
|
||||||
|
hstring ImagePathVM();
|
||||||
|
int32_t TypeVM();
|
||||||
|
bool CheckedVM();
|
||||||
|
void CheckedVM(bool value);
|
||||||
|
int32_t StateVM();
|
||||||
|
|
||||||
|
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
|
||||||
|
void PropertyChanged(winrt::event_token const& token) noexcept;
|
||||||
|
|
||||||
|
uint32_t _index = 0;
|
||||||
|
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::factory_implementation
|
||||||
|
{
|
||||||
|
struct ExplorerItemViewModel : ExplorerItemViewModelT<ExplorerItemViewModel, implementation::ExplorerItemViewModel>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
namespace PowerRenameUI
|
||||||
|
{
|
||||||
|
[bindable] [default_interface] runtimeclass ExplorerItemViewModel :
|
||||||
|
Microsoft.UI.Xaml.Data.INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
ExplorerItemViewModel();
|
||||||
|
|
||||||
|
Int32 IdVM { get; };
|
||||||
|
String IdStrVM { get; };
|
||||||
|
String OriginalVM { get; };
|
||||||
|
String RenamedVM { get; };
|
||||||
|
Double IndentationVM { get; };
|
||||||
|
String ImagePathVM { get; };
|
||||||
|
Int32 TypeVM { get; };
|
||||||
|
Boolean CheckedVM;
|
||||||
|
Int32 StateVM { get; };
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include "ExplorerItemsSource.h"
|
||||||
|
#if __has_include("ExplorerItemsSource.g.cpp")
|
||||||
|
#include "ExplorerItemsSource.g.cpp"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
using namespace winrt;
|
||||||
|
using namespace Windows::UI::Xaml;
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::implementation
|
||||||
|
{
|
||||||
|
}
|
||||||
287
src/modules/powerrename/PowerRenameUILib/ExplorerItemsSource.h
Normal file
287
src/modules/powerrename/PowerRenameUILib/ExplorerItemsSource.h
Normal file
@@ -0,0 +1,287 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "winrt/Windows.UI.Xaml.h"
|
||||||
|
#include "winrt/Windows.UI.Xaml.Markup.h"
|
||||||
|
#include "winrt/Windows.UI.Xaml.Interop.h"
|
||||||
|
#include "winrt/Windows.UI.Xaml.Controls.Primitives.h"
|
||||||
|
#include "ExplorerItemsSource.g.h"
|
||||||
|
#include "ExplorerItemViewModel.h"
|
||||||
|
|
||||||
|
#include <PowerRenameInterfaces.h>
|
||||||
|
|
||||||
|
using winrt::Microsoft::UI::Xaml::Data::ItemIndexRange;
|
||||||
|
using winrt::Windows::Foundation::IInspectable;
|
||||||
|
using namespace winrt::Windows::Foundation::Collections;
|
||||||
|
|
||||||
|
extern CComPtr<IPowerRenameManager> g_prManager;
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::implementation
|
||||||
|
{
|
||||||
|
struct ExplorerItemProxyIterator
|
||||||
|
{
|
||||||
|
using iterator_category = std::random_access_iterator_tag;
|
||||||
|
using value_type = IInspectable;
|
||||||
|
using difference_type = std::ptrdiff_t;
|
||||||
|
using pointer = uint32_t;
|
||||||
|
using reference = IInspectable;
|
||||||
|
|
||||||
|
const bool* _filtered = nullptr;
|
||||||
|
|
||||||
|
ExplorerItemProxyIterator(pointer ptr, const bool* filtered) :
|
||||||
|
_index(ptr), _filtered{ filtered } {}
|
||||||
|
|
||||||
|
reference operator*() const
|
||||||
|
{
|
||||||
|
const uint32_t realIndex = g_prManager->GetVisibleItemRealIndex(_index);
|
||||||
|
if (*_filtered)
|
||||||
|
{
|
||||||
|
return winrt::make<ExplorerItemViewModel>(realIndex);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return winrt::make<ExplorerItemViewModel>(_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ExplorerItemProxyIterator& operator++()
|
||||||
|
{
|
||||||
|
_index++;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ExplorerItemProxyIterator operator++(int)
|
||||||
|
{
|
||||||
|
ExplorerItemProxyIterator tmp = *this;
|
||||||
|
++(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
ExplorerItemProxyIterator& operator--()
|
||||||
|
{
|
||||||
|
_index--;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ExplorerItemProxyIterator operator--(int)
|
||||||
|
{
|
||||||
|
ExplorerItemProxyIterator tmp = *this;
|
||||||
|
--(*this);
|
||||||
|
return tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
friend bool operator==(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index == b._index; };
|
||||||
|
friend bool operator!=(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index != b._index; };
|
||||||
|
|
||||||
|
ExplorerItemProxyIterator& operator+=(difference_type n)
|
||||||
|
{
|
||||||
|
_index += static_cast<pointer>(n);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ExplorerItemProxyIterator operator+(difference_type n) const { return ExplorerItemProxyIterator{ static_cast<pointer>(_index + n), _filtered }; }
|
||||||
|
friend ExplorerItemProxyIterator operator+(difference_type n, const ExplorerItemProxyIterator& iter) { return ExplorerItemProxyIterator(static_cast<pointer>(n + iter._index), iter._filtered); }
|
||||||
|
|
||||||
|
ExplorerItemProxyIterator& operator-=(difference_type n)
|
||||||
|
{
|
||||||
|
_index -= static_cast<pointer>(n);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
ExplorerItemProxyIterator operator-(difference_type n) const { return ExplorerItemProxyIterator{ static_cast<pointer>(_index - n), _filtered }; }
|
||||||
|
difference_type operator-(const ExplorerItemProxyIterator& iter) const { return _index - iter._index; }
|
||||||
|
|
||||||
|
friend bool operator<(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index < b._index; }
|
||||||
|
friend bool operator>(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index > b._index; }
|
||||||
|
friend bool operator<=(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index <= b._index; }
|
||||||
|
friend bool operator>=(const ExplorerItemProxyIterator& a, const ExplorerItemProxyIterator& b) { return a._index >= b._index; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
pointer _index;
|
||||||
|
};
|
||||||
|
|
||||||
|
using ItemT = IInspectable;
|
||||||
|
struct ExplorerItemsSource : ExplorerItemsSourceT<ExplorerItemsSource>, winrt::vector_view_base<ExplorerItemsSource, ItemT>
|
||||||
|
{
|
||||||
|
bool filtered = false;
|
||||||
|
|
||||||
|
struct Container
|
||||||
|
{
|
||||||
|
ExplorerItemProxyIterator first;
|
||||||
|
ExplorerItemProxyIterator last;
|
||||||
|
|
||||||
|
auto begin() const noexcept
|
||||||
|
{
|
||||||
|
return first;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto end() const noexcept
|
||||||
|
{
|
||||||
|
return last;
|
||||||
|
}
|
||||||
|
} container = { { {}, {} }, { {}, {} } };
|
||||||
|
|
||||||
|
void SetIsFiltered(const bool value)
|
||||||
|
{
|
||||||
|
filtered = value;
|
||||||
|
|
||||||
|
if (!g_prManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
uint32_t item_count = 0;
|
||||||
|
if (value)
|
||||||
|
winrt::check_hresult(g_prManager->GetVisibleItemCount(&item_count));
|
||||||
|
else
|
||||||
|
winrt::check_hresult(g_prManager->GetItemCount(&item_count));
|
||||||
|
|
||||||
|
container = Container{ { 0, &filtered }, { item_count, &filtered } };
|
||||||
|
}
|
||||||
|
|
||||||
|
auto& get_container() const noexcept
|
||||||
|
{
|
||||||
|
return container;
|
||||||
|
}
|
||||||
|
|
||||||
|
// IObservableVector. We don't need a full implementation, since only the items' properties changes
|
||||||
|
|
||||||
|
Windows::Foundation::Collections::IVectorView<ItemT> GetView() const noexcept
|
||||||
|
{
|
||||||
|
return static_cast<ExplorerItemsSource const&>(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::event_token VectorChanged(Windows::Foundation::Collections::VectorChangedEventHandler<ItemT> const& handler)
|
||||||
|
{
|
||||||
|
return m_changed.add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void VectorChanged(event_token const cookie)
|
||||||
|
{
|
||||||
|
m_changed.remove(cookie);
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::event<Windows::Foundation::Collections::VectorChangedEventHandler<ItemT>> m_changed;
|
||||||
|
|
||||||
|
struct args : implements<args, Windows::Foundation::Collections::IVectorChangedEventArgs>
|
||||||
|
{
|
||||||
|
args(Windows::Foundation::Collections::CollectionChange const change, uint32_t const index) noexcept :
|
||||||
|
m_change(change),
|
||||||
|
m_index(index)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
Windows::Foundation::Collections::CollectionChange CollectionChange() const noexcept
|
||||||
|
{
|
||||||
|
return m_change;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t Index() const noexcept
|
||||||
|
{
|
||||||
|
return m_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Windows::Foundation::Collections::CollectionChange const m_change;
|
||||||
|
uint32_t const m_index;
|
||||||
|
};
|
||||||
|
|
||||||
|
void call_changed(Windows::Foundation::Collections::CollectionChange const change, uint32_t const index)
|
||||||
|
{
|
||||||
|
m_changed(static_cast<ExplorerItemsSource const&>(*this), make<args>(change, index));
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidateCollection()
|
||||||
|
{
|
||||||
|
SetIsFiltered(filtered);
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::Reset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InvalidateItemRange(uint32_t const startIdx, uint32_t const count)
|
||||||
|
{
|
||||||
|
for (uint32_t index = startIdx; index < startIdx + count; ++index)
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemChanged, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetAt(uint32_t const index, ItemT const& /*value*/)
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemChanged, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InsertAt(uint32_t const index, ItemT const& /*value*/)
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemInserted, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAt(uint32_t const index)
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemRemoved, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Append(ItemT const& /*value*/)
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemInserted, this->Size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveAtEnd()
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::ItemRemoved, this->Size());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Clear()
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::Reset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReplaceAll(array_view<ItemT const> /*value*/)
|
||||||
|
{
|
||||||
|
call_changed(Windows::Foundation::Collections::CollectionChange::Reset, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IClosable
|
||||||
|
void Close() noexcept
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISelectionInfo
|
||||||
|
void SelectRange(const ItemIndexRange& /*itemIndexRange*/) noexcept
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DeselectRange(const ItemIndexRange& /*itemIndexRange*/) noexcept
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSelected(int32_t /*index*/) noexcept
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
IVectorView<ItemIndexRange> GetSelectedRanges() noexcept
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
// IItemsRangeInfo
|
||||||
|
void RangesChanged(const ItemIndexRange& /*visibleRange*/, const IVectorView<ItemIndexRange>& /*trackedItems*/)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
//INotifyPropertyChanged
|
||||||
|
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
|
||||||
|
{
|
||||||
|
return m_propertyChanged.add(handler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PropertyChanged(winrt::event_token const& token) noexcept
|
||||||
|
{
|
||||||
|
m_propertyChanged.remove(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
||||||
|
|
||||||
|
void RaisePropertyChanged(hstring const& propertyName)
|
||||||
|
{
|
||||||
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs(propertyName));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace winrt::PowerRenameUI::factory_implementation
|
||||||
|
{
|
||||||
|
struct ExplorerItemsSource : ExplorerItemsSourceT<ExplorerItemsSource, implementation::ExplorerItemsSource>
|
||||||
|
{
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace PowerRenameUI
|
||||||
|
{
|
||||||
|
[default_interface] runtimeclass ExplorerItemsSource :
|
||||||
|
Microsoft.UI.Xaml.Data.IItemsRangeInfo,
|
||||||
|
Microsoft.UI.Xaml.Data.ISelectionInfo,
|
||||||
|
Windows.Foundation.Collections.IVectorView<IInspectable>,
|
||||||
|
Windows.Foundation.Collections.IObservableVector<IInspectable>,
|
||||||
|
Microsoft.UI.Xaml.Data.INotifyPropertyChanged
|
||||||
|
{
|
||||||
|
ExplorerItemsSource();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import "ExplorerItem.idl";
|
import "ExplorerItem.idl";
|
||||||
|
import "ExplorerItemsSource.idl";
|
||||||
import "PatternSnippet.idl";
|
import "PatternSnippet.idl";
|
||||||
|
|
||||||
namespace PowerRenameUI
|
namespace PowerRenameUI
|
||||||
@@ -10,7 +11,7 @@ namespace PowerRenameUI
|
|||||||
Windows.Foundation.Collections.IObservableVector<String> SearchMRU { get; };
|
Windows.Foundation.Collections.IObservableVector<String> SearchMRU { get; };
|
||||||
Windows.Foundation.Collections.IObservableVector<String> ReplaceMRU { get; };
|
Windows.Foundation.Collections.IObservableVector<String> ReplaceMRU { get; };
|
||||||
|
|
||||||
Windows.Foundation.Collections.IObservableVector<ExplorerItem> ExplorerItems { get; };
|
ExplorerItemsSource ExplorerItems { get; };
|
||||||
Windows.Foundation.Collections.IObservableVector<PatternSnippet> SearchRegExShortcuts { get; };
|
Windows.Foundation.Collections.IObservableVector<PatternSnippet> SearchRegExShortcuts { get; };
|
||||||
Windows.Foundation.Collections.IObservableVector<PatternSnippet> DateTimeShortcuts { get; };
|
Windows.Foundation.Collections.IObservableVector<PatternSnippet> DateTimeShortcuts { get; };
|
||||||
|
|
||||||
|
|||||||
@@ -48,6 +48,7 @@
|
|||||||
Checked="SelectAll"
|
Checked="SelectAll"
|
||||||
Content=""
|
Content=""
|
||||||
IsChecked="True"
|
IsChecked="True"
|
||||||
|
Style="{StaticResource CheckBoxDefaultStyleOverride}"
|
||||||
Unchecked="SelectAll" />
|
Unchecked="SelectAll" />
|
||||||
<Image
|
<Image
|
||||||
Width="16"
|
Width="16"
|
||||||
@@ -112,6 +113,7 @@
|
|||||||
HorizontalAlignment="Stretch"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalAlignment="Bottom"
|
VerticalAlignment="Bottom"
|
||||||
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
Fill="{ThemeResource DividerStrokeColorDefaultBrush}" />
|
||||||
|
<!--Changing VirtualizationMode to Recycling introduces the issue with popping indent/icons, but makes scrolling slightly faster -->
|
||||||
<ListView
|
<ListView
|
||||||
Grid.Row="1"
|
Grid.Row="1"
|
||||||
Grid.ColumnSpan="6"
|
Grid.ColumnSpan="6"
|
||||||
@@ -119,7 +121,29 @@
|
|||||||
IsTabStop="false"
|
IsTabStop="false"
|
||||||
ItemsSource="{x:Bind ExplorerItems, Mode=OneWay}"
|
ItemsSource="{x:Bind ExplorerItems, Mode=OneWay}"
|
||||||
SelectionMode="None"
|
SelectionMode="None"
|
||||||
XYFocusKeyboardNavigation="Enabled">
|
x:Name="listView_ExplorerItems"
|
||||||
|
XYFocusKeyboardNavigation="Enabled"
|
||||||
|
VirtualizingStackPanel.VirtualizationMode="Standard">
|
||||||
|
<ListView.ItemTemplate>
|
||||||
|
<DataTemplate x:DataType="local:ExplorerItemViewModel">
|
||||||
|
<local:ExplorerItem
|
||||||
|
Id="{x:Bind IdVM, Mode=OneWay}"
|
||||||
|
IdStr="{x:Bind IdStrVM}"
|
||||||
|
Original="{x:Bind OriginalVM}"
|
||||||
|
Renamed="{x:Bind RenamedVM}"
|
||||||
|
Indentation="{x:Bind IndentationVM}"
|
||||||
|
ImagePath="{x:Bind ImagePathVM}"
|
||||||
|
Type="{x:Bind TypeVM}"
|
||||||
|
Checked="{x:Bind CheckedVM, Mode=TwoWay, FallbackValue=True}"
|
||||||
|
State="{x:Bind StateVM}" />
|
||||||
|
</DataTemplate >
|
||||||
|
</ListView.ItemTemplate>
|
||||||
|
<ListView.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<VirtualizingStackPanel />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ListView.ItemsPanel>
|
||||||
|
|
||||||
<ListView.ItemContainerStyle>
|
<ListView.ItemContainerStyle>
|
||||||
<Style TargetType="ListViewItem">
|
<Style TargetType="ListViewItem">
|
||||||
<Setter Property="IsTabStop" Value="False" />
|
<Setter Property="IsTabStop" Value="False" />
|
||||||
|
|||||||
@@ -28,7 +28,6 @@
|
|||||||
using namespace winrt;
|
using namespace winrt;
|
||||||
using namespace Windows::UI::Xaml;
|
using namespace Windows::UI::Xaml;
|
||||||
using namespace winrt::Microsoft::Windows::ApplicationModel::Resources;
|
using namespace winrt::Microsoft::Windows::ApplicationModel::Resources;
|
||||||
|
|
||||||
// Non-localizable
|
// Non-localizable
|
||||||
const std::wstring PowerRenameUIIco = L"PowerRenameUI.ico";
|
const std::wstring PowerRenameUIIco = L"PowerRenameUI.ico";
|
||||||
const wchar_t c_WindowClass[] = L"PowerRename";
|
const wchar_t c_WindowClass[] = L"PowerRename";
|
||||||
@@ -40,13 +39,65 @@ extern std::vector<std::wstring> g_files;
|
|||||||
ThemeListener theme_listener{};
|
ThemeListener theme_listener{};
|
||||||
HWND CurrentWindow;
|
HWND CurrentWindow;
|
||||||
|
|
||||||
void handleTheme() {
|
void handleTheme()
|
||||||
|
{
|
||||||
auto theme = theme_listener.AppTheme;
|
auto theme = theme_listener.AppTheme;
|
||||||
auto isDark = theme == AppTheme::Dark;
|
auto isDark = theme == AppTheme::Dark;
|
||||||
Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
|
Logger::info(L"Theme is now {}", isDark ? L"Dark" : L"Light");
|
||||||
ThemeHelpers::SetImmersiveDarkMode(CurrentWindow, isDark);
|
ThemeHelpers::SetImmersiveDarkMode(CurrentWindow, isDark);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Disabled for now, because it causes indentation and icons to appear "asynchronously"
|
||||||
|
// #define ENABLE_RECYCLING_VIRTUALIZATION_MODE
|
||||||
|
|
||||||
|
CComPtr<IPowerRenameManager> g_prManager;
|
||||||
|
std::function<void(void)> g_itemToggledCallback;
|
||||||
|
|
||||||
|
winrt::Microsoft::UI::Xaml::Controls::ScrollViewer FindScrollViewer(winrt::Microsoft::UI::Xaml::DependencyObject start)
|
||||||
|
{
|
||||||
|
int childrenCount = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(start);
|
||||||
|
for (int i = 0; i < childrenCount; i++)
|
||||||
|
{
|
||||||
|
auto obj = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChild(start, i);
|
||||||
|
|
||||||
|
if (auto sv = obj.try_as<winrt::Microsoft::UI::Xaml::Controls::ScrollViewer>())
|
||||||
|
{
|
||||||
|
return sv;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
auto result = FindScrollViewer(obj);
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
PowerRenameUI::ExplorerItem FindFirstExplorerItemRecursive(winrt::Microsoft::UI::Xaml::DependencyObject startNode)
|
||||||
|
{
|
||||||
|
auto explorerItem = startNode.try_as<PowerRenameUI::ExplorerItem>();
|
||||||
|
if (explorerItem)
|
||||||
|
{
|
||||||
|
return explorerItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
int childCount = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChildrenCount(startNode);
|
||||||
|
for (int i = 0; i < childCount; ++i)
|
||||||
|
{
|
||||||
|
auto child = winrt::Microsoft::UI::Xaml::Media::VisualTreeHelper::GetChild(startNode, i);
|
||||||
|
explorerItem = FindFirstExplorerItemRecursive(child);
|
||||||
|
if (explorerItem)
|
||||||
|
{
|
||||||
|
return explorerItem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
namespace winrt::PowerRenameUI::implementation
|
namespace winrt::PowerRenameUI::implementation
|
||||||
{
|
{
|
||||||
MainWindow::MainWindow() :
|
MainWindow::MainWindow() :
|
||||||
@@ -71,7 +122,7 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
POINT cursorPosition{};
|
POINT cursorPosition{};
|
||||||
if (GetCursorPos(&cursorPosition))
|
if (GetCursorPos(&cursorPosition))
|
||||||
{
|
{
|
||||||
::Windows::Graphics::PointInt32 point{ cursorPosition.x, cursorPosition.y};
|
::Windows::Graphics::PointInt32 point{ cursorPosition.x, cursorPosition.y };
|
||||||
Microsoft::UI::Windowing::DisplayArea displayArea = Microsoft::UI::Windowing::DisplayArea::GetFromPoint(point, Microsoft::UI::Windowing::DisplayAreaFallback::Nearest);
|
Microsoft::UI::Windowing::DisplayArea displayArea = Microsoft::UI::Windowing::DisplayArea::GetFromPoint(point, Microsoft::UI::Windowing::DisplayAreaFallback::Nearest);
|
||||||
|
|
||||||
HMONITOR hMonitor = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY);
|
HMONITOR hMonitor = MonitorFromPoint(cursorPosition, MONITOR_DEFAULTTOPRIMARY);
|
||||||
@@ -97,11 +148,19 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
}
|
}
|
||||||
|
|
||||||
Title(hstring{ L"PowerRename" });
|
Title(hstring{ L"PowerRename" });
|
||||||
|
|
||||||
m_searchMRUList = winrt::single_threaded_observable_vector<hstring>();
|
m_searchMRUList = winrt::single_threaded_observable_vector<hstring>();
|
||||||
m_replaceMRUList = winrt::single_threaded_observable_vector<hstring>();
|
m_replaceMRUList = winrt::single_threaded_observable_vector<hstring>();
|
||||||
|
|
||||||
m_explorerItems = winrt::single_threaded_observable_vector<PowerRenameUI::ExplorerItem>();
|
std::function<void(void)> toggledCallback = [this] {
|
||||||
|
_TRACER_;
|
||||||
|
UpdateCounts();
|
||||||
|
};
|
||||||
|
|
||||||
|
g_itemToggledCallback.swap(toggledCallback);
|
||||||
|
|
||||||
|
m_explorerItems = winrt::make<ExplorerItemsSource>();
|
||||||
|
get_self<ExplorerItemsSource>(m_explorerItems)->SetIsFiltered(false);
|
||||||
|
|
||||||
m_searchRegExShortcuts = winrt::single_threaded_observable_vector<PowerRenameUI::PatternSnippet>();
|
m_searchRegExShortcuts = winrt::single_threaded_observable_vector<PowerRenameUI::PatternSnippet>();
|
||||||
auto factory = winrt::get_activation_factory<ResourceManager, IResourceManagerFactory>();
|
auto factory = winrt::get_activation_factory<ResourceManager, IResourceManagerFactory>();
|
||||||
@@ -139,8 +198,34 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
|
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
|
|
||||||
|
listView_ExplorerItems().ApplyTemplate();
|
||||||
|
#ifdef ENABLE_RECYCLING_VIRTUALIZATION_MODE
|
||||||
|
if (auto scrollViewer = FindScrollViewer(listView_ExplorerItems()); scrollViewer)
|
||||||
|
{
|
||||||
|
Microsoft::UI::Xaml::DispatcherTimer debounceTimer = nullptr;
|
||||||
|
|
||||||
|
scrollViewer.ViewChanged([this, scrollViewer, timer = std::move(debounceTimer)](IInspectable const&, Microsoft::UI::Xaml::Controls::ScrollViewerViewChangedEventArgs const&) mutable {
|
||||||
|
if (!timer)
|
||||||
|
{
|
||||||
|
timer = {};
|
||||||
|
timer.Tick([this, &timer](auto&, auto&) {
|
||||||
|
get_self<ExplorerItemsSource>(m_explorerItems)->InvalidateCollection();
|
||||||
|
timer.Stop();
|
||||||
|
timer = nullptr;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
winrt::Windows::Foundation::TimeSpan timeSpan{ 200ms };
|
||||||
|
timer.Interval(timeSpan);
|
||||||
|
timer.Start();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
#endif
|
||||||
if (SUCCEEDED(CPowerRenameManager::s_CreateInstance(&m_prManager)))
|
if (SUCCEEDED(CPowerRenameManager::s_CreateInstance(&m_prManager)))
|
||||||
{
|
{
|
||||||
|
g_prManager = m_prManager;
|
||||||
// Create the factory for our items
|
// Create the factory for our items
|
||||||
CComPtr<IPowerRenameItemFactory> prItemFactory;
|
CComPtr<IPowerRenameItemFactory> prItemFactory;
|
||||||
if (SUCCEEDED(CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&prItemFactory))))
|
if (SUCCEEDED(CPowerRenameItem::s_CreateInstance(nullptr, IID_PPV_ARGS(&prItemFactory))))
|
||||||
@@ -150,13 +235,10 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
if (SUCCEEDED(m_prManager->Advise(&m_managerEvents, &m_cookie)))
|
if (SUCCEEDED(m_prManager->Advise(&m_managerEvents, &m_cookie)))
|
||||||
{
|
{
|
||||||
CComPtr<IShellItemArray> shellItemArray;
|
CComPtr<IShellItemArray> shellItemArray;
|
||||||
// To test PowerRename uncomment this line and update the path to
|
// To test PowerRename uncomment DEBUG_BENCHMARK_100K_ENTRIES define
|
||||||
// your local (absolute or relative) path which you want to see in PowerRename
|
|
||||||
// g_files.push_back(<path>);
|
|
||||||
|
|
||||||
if (!g_files.empty())
|
if (!g_files.empty())
|
||||||
{
|
{
|
||||||
if (SUCCEEDED(CreateShellItemArrayFromPaths(g_files, &shellItemArray)))
|
if (SUCCEEDED(CreateShellItemArrayFromPaths(std::move(g_files), &shellItemArray)))
|
||||||
{
|
{
|
||||||
CComPtr<IEnumShellItems> enumShellItems;
|
CComPtr<IEnumShellItems> enumShellItems;
|
||||||
if (SUCCEEDED(shellItemArray->EnumItems(&enumShellItems)))
|
if (SUCCEEDED(shellItemArray->EnumItems(&enumShellItems)))
|
||||||
@@ -195,6 +277,12 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
button_rename().IsEnabled(false);
|
button_rename().IsEnabled(false);
|
||||||
InitAutoComplete();
|
InitAutoComplete();
|
||||||
SearchReplaceChanged();
|
SearchReplaceChanged();
|
||||||
|
InvalidateItemListViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MainWindow::InvalidateItemListViewState()
|
||||||
|
{
|
||||||
|
get_self<ExplorerItemsSource>(m_explorerItems)->InvalidateCollection();
|
||||||
}
|
}
|
||||||
|
|
||||||
winrt::event_token MainWindow::PropertyChanged(Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
|
winrt::event_token MainWindow::PropertyChanged(Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler)
|
||||||
@@ -209,7 +297,9 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
|
|
||||||
hstring MainWindow::OriginalCount()
|
hstring MainWindow::OriginalCount()
|
||||||
{
|
{
|
||||||
return hstring{ std::to_wstring(m_explorerItems.Size()) };
|
UINT count = 0;
|
||||||
|
m_prManager->GetItemCount(&count);
|
||||||
|
return hstring{ std::to_wstring(count) };
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::OriginalCount(hstring)
|
void MainWindow::OriginalCount(hstring)
|
||||||
@@ -227,46 +317,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"RenamedCount" });
|
m_propertyChanged(*this, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs{ L"RenamedCount" });
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::AddExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked)
|
|
||||||
{
|
|
||||||
auto newItem = winrt::make<PowerRenameUI::implementation::ExplorerItem>(id, original, renamed, type, depth, checked);
|
|
||||||
newItem.PropertyChanged([this](Windows::Foundation::IInspectable const& sender, Microsoft::UI::Xaml::Data::PropertyChangedEventArgs const& e) {
|
|
||||||
auto item = sender.as<ExplorerItem>();
|
|
||||||
std::wstring property{ e.PropertyName() };
|
|
||||||
|
|
||||||
if (item && property == L"Checked")
|
|
||||||
{
|
|
||||||
ToggleItem(item->Id(), item->Checked());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
m_explorerItems.Append(newItem);
|
|
||||||
m_explorerItemsMap[id] = newItem;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::UpdateExplorerItem(int32_t id, std::optional<hstring> newOriginalName, std::optional<hstring> newName, PowerRenameItemRenameStatus itemStatus)
|
|
||||||
{
|
|
||||||
auto itemToUpdate = FindById(id);
|
|
||||||
if (itemToUpdate != NULL)
|
|
||||||
{
|
|
||||||
if (newOriginalName.has_value())
|
|
||||||
{
|
|
||||||
itemToUpdate.Original(*newOriginalName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newName.has_value())
|
|
||||||
{
|
|
||||||
itemToUpdate.Renamed(*newName);
|
|
||||||
}
|
|
||||||
|
|
||||||
itemToUpdate.State(static_cast<int32_t>(itemStatus));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PowerRenameUI::ExplorerItem MainWindow::FindById(int32_t id)
|
|
||||||
{
|
|
||||||
return m_explorerItemsMap.contains(id) ? m_explorerItemsMap[id] : NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void MainWindow::SelectAll(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
void MainWindow::SelectAll(winrt::Windows::Foundation::IInspectable const&, winrt::Microsoft::UI::Xaml::RoutedEventArgs const&)
|
||||||
{
|
{
|
||||||
if (checkBox_selectAll().IsChecked().GetBoolean() != m_allSelected)
|
if (checkBox_selectAll().IsChecked().GetBoolean() != m_allSelected)
|
||||||
@@ -275,11 +325,9 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
m_allSelected = !m_allSelected;
|
m_allSelected = !m_allSelected;
|
||||||
if (button_showRenamed().IsChecked())
|
if (button_showRenamed().IsChecked())
|
||||||
{
|
{
|
||||||
m_explorerItems.Clear();
|
|
||||||
m_explorerItemsMap.clear();
|
|
||||||
PopulateExplorerItems();
|
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
}
|
}
|
||||||
|
InvalidateItemListViewState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,11 +340,9 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
m_prManager->GetFilter(&filter);
|
m_prManager->GetFilter(&filter);
|
||||||
if (filter != PowerRenameFilters::None)
|
if (filter != PowerRenameFilters::None)
|
||||||
{
|
{
|
||||||
m_explorerItems.Clear();
|
|
||||||
m_explorerItemsMap.clear();
|
|
||||||
m_prManager->SwitchFilter(0);
|
m_prManager->SwitchFilter(0);
|
||||||
PopulateExplorerItems();
|
get_self<ExplorerItemsSource>(m_explorerItems)->SetIsFiltered(false);
|
||||||
UpdateCounts();
|
InvalidateItemListViewState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,11 +355,10 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
m_prManager->GetFilter(&filter);
|
m_prManager->GetFilter(&filter);
|
||||||
if (filter != PowerRenameFilters::ShouldRename)
|
if (filter != PowerRenameFilters::ShouldRename)
|
||||||
{
|
{
|
||||||
m_explorerItems.Clear();
|
|
||||||
m_explorerItemsMap.clear();
|
|
||||||
m_prManager->SwitchFilter(0);
|
m_prManager->SwitchFilter(0);
|
||||||
PopulateExplorerItems();
|
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
|
get_self<ExplorerItemsSource>(m_explorerItems)->SetIsFiltered(true);
|
||||||
|
InvalidateItemListViewState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -393,42 +438,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::PopulateExplorerItems()
|
|
||||||
{
|
|
||||||
_TRACER_;
|
|
||||||
|
|
||||||
UINT count = 0;
|
|
||||||
m_prManager->GetVisibleItemCount(&count);
|
|
||||||
Logger::debug(L"Number of visible items: {}", count);
|
|
||||||
|
|
||||||
for (UINT i = 0; i < count; ++i)
|
|
||||||
{
|
|
||||||
CComPtr<IPowerRenameItem> renameItem;
|
|
||||||
if (SUCCEEDED(m_prManager->GetVisibleItemByIndex(i, &renameItem)))
|
|
||||||
{
|
|
||||||
int id = 0;
|
|
||||||
renameItem->GetId(&id);
|
|
||||||
|
|
||||||
PWSTR originalName = nullptr;
|
|
||||||
renameItem->GetOriginalName(&originalName);
|
|
||||||
PWSTR newName = nullptr;
|
|
||||||
renameItem->GetNewName(&newName);
|
|
||||||
|
|
||||||
bool selected;
|
|
||||||
renameItem->GetSelected(&selected);
|
|
||||||
|
|
||||||
UINT depth = 0;
|
|
||||||
renameItem->GetDepth(&depth);
|
|
||||||
|
|
||||||
bool isFolder = false;
|
|
||||||
winrt::check_hresult(renameItem->GetIsFolder(&isFolder));
|
|
||||||
|
|
||||||
AddExplorerItem(
|
|
||||||
id, originalName, newName == nullptr ? hstring{} : hstring{ newName }, isFolder ? 0 : 1, depth, selected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT MainWindow::InitAutoComplete()
|
HRESULT MainWindow::InitAutoComplete()
|
||||||
{
|
{
|
||||||
_TRACER_;
|
_TRACER_;
|
||||||
@@ -573,6 +582,8 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
{
|
{
|
||||||
m_prManager->PutFlags(flags);
|
m_prManager->PutFlags(flags);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InvalidateItemListViewState();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MainWindow::SetHandlers()
|
void MainWindow::SetHandlers()
|
||||||
@@ -742,11 +753,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
if (SUCCEEDED(m_prManager->GetItemByIndex(i, &spItem)))
|
if (SUCCEEDED(m_prManager->GetItemByIndex(i, &spItem)))
|
||||||
{
|
{
|
||||||
spItem->PutSelected(selected);
|
spItem->PutSelected(selected);
|
||||||
int id = 0;
|
|
||||||
spItem->GetId(&id);
|
|
||||||
auto item = FindById(id);
|
|
||||||
if (item)
|
|
||||||
item.Checked(selected);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
@@ -757,7 +763,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
_TRACER_;
|
_TRACER_;
|
||||||
|
|
||||||
m_prManager->SwitchFilter(0);
|
m_prManager->SwitchFilter(0);
|
||||||
PopulateExplorerItems();
|
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -976,73 +981,11 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
button_rename().IsEnabled(renamingCount > 0);
|
button_rename().IsEnabled(renamingCount > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
OriginalCount(hstring{ std::to_wstring(m_explorerItems.Size()) });
|
|
||||||
RenamedCount(hstring{ std::to_wstring(m_renamingCount) });
|
RenamedCount(hstring{ std::to_wstring(m_renamingCount) });
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT MainWindow::OnItemAdded(_In_ IPowerRenameItem* renameItem)
|
HRESULT MainWindow::OnRename(_In_ IPowerRenameItem* /*renameItem*/)
|
||||||
{
|
{
|
||||||
int id = 0;
|
|
||||||
renameItem->GetId(&id);
|
|
||||||
|
|
||||||
PWSTR originalName = nullptr;
|
|
||||||
renameItem->GetOriginalName(&originalName);
|
|
||||||
PWSTR newName = nullptr;
|
|
||||||
renameItem->GetNewName(&newName);
|
|
||||||
|
|
||||||
bool selected;
|
|
||||||
renameItem->GetSelected(&selected);
|
|
||||||
|
|
||||||
UINT depth = 0;
|
|
||||||
renameItem->GetDepth(&depth);
|
|
||||||
|
|
||||||
bool isFolder = false;
|
|
||||||
winrt::check_hresult(renameItem->GetIsFolder(&isFolder));
|
|
||||||
|
|
||||||
AddExplorerItem(
|
|
||||||
id, originalName, newName == nullptr ? hstring{} : hstring{ newName }, isFolder ? 0 : 1, depth, selected);
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT MainWindow::OnUpdate(_In_ IPowerRenameItem* renameItem)
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
HRESULT hr = renameItem->GetId(&id);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
PWSTR newName = nullptr;
|
|
||||||
hr = renameItem->GetNewName(&newName);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
PowerRenameItemRenameStatus status;
|
|
||||||
hr = renameItem->GetStatus(&status);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hstring newNameStr = newName == nullptr ? hstring{} : newName;
|
|
||||||
UpdateExplorerItem(id, std::nullopt, newNameStr, status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
HRESULT MainWindow::OnRename(_In_ IPowerRenameItem* renameItem)
|
|
||||||
{
|
|
||||||
int id;
|
|
||||||
HRESULT hr = renameItem->GetId(&id);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
PWSTR newName = nullptr;
|
|
||||||
hr = renameItem->GetOriginalName(&newName);
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
hstring newNameStr = newName == nullptr ? hstring{} : newName;
|
|
||||||
UpdateExplorerItem(id, newNameStr, L"", PowerRenameItemRenameStatus::Init);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
@@ -1055,18 +998,9 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
{
|
{
|
||||||
m_flagValidationInProgress = false;
|
m_flagValidationInProgress = false;
|
||||||
}
|
}
|
||||||
else
|
|
||||||
{
|
|
||||||
DWORD filter = 0;
|
|
||||||
m_prManager->GetFilter(&filter);
|
|
||||||
if (filter == PowerRenameFilters::ShouldRename)
|
|
||||||
{
|
|
||||||
m_explorerItems.Clear();
|
|
||||||
PopulateExplorerItems();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateCounts();
|
UpdateCounts();
|
||||||
|
InvalidateItemListViewState();
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1078,14 +1012,16 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
if (closeUIWindowAfterRenaming)
|
if (closeUIWindowAfterRenaming)
|
||||||
{
|
{
|
||||||
// Close the window
|
// Close the window
|
||||||
PostMessage(m_window, WM_CLOSE, static_cast<WPARAM>(0), static_cast<LPARAM>(0));
|
PostMessage(m_window, WM_CLOSE, static_cast<WPARAM>(0), static_cast<LPARAM>(0));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Force renaming work to start so newly renamed items are processed right away
|
// Force renaming work to start so newly renamed items are processed right away
|
||||||
SearchReplaceChanged(true);
|
SearchReplaceChanged(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InvalidateItemListViewState();
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "MainWindow.g.h"
|
#include "MainWindow.g.h"
|
||||||
#include "PatternSnippet.h"
|
#include "PatternSnippet.h"
|
||||||
#include "ExplorerItem.h"
|
#include "ExplorerItem.h"
|
||||||
|
#include "ExplorerItemsSource.h"
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <wil/resource.h>
|
#include <wil/resource.h>
|
||||||
@@ -58,8 +59,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
return QISearch(this, qit, riid, ppv);
|
return QISearch(this, qit, riid, ppv);
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT OnItemAdded(_In_ IPowerRenameItem* renameItem) override { return m_app->OnItemAdded(renameItem); }
|
|
||||||
HRESULT OnUpdate(_In_ IPowerRenameItem* renameItem) override { return m_app->OnUpdate(renameItem); }
|
|
||||||
HRESULT OnRename(_In_ IPowerRenameItem* renameItem) override { return m_app->OnRename(renameItem); }
|
HRESULT OnRename(_In_ IPowerRenameItem* renameItem) override { return m_app->OnRename(renameItem); }
|
||||||
HRESULT OnError(_In_ IPowerRenameItem* renameItem) override { return m_app->OnError(renameItem); }
|
HRESULT OnError(_In_ IPowerRenameItem* renameItem) override { return m_app->OnError(renameItem); }
|
||||||
HRESULT OnRegExStarted(_In_ DWORD threadId) override { return m_app->OnRegExStarted(threadId); }
|
HRESULT OnRegExStarted(_In_ DWORD threadId) override { return m_app->OnRegExStarted(threadId); }
|
||||||
@@ -75,9 +74,11 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
|
|
||||||
MainWindow();
|
MainWindow();
|
||||||
|
|
||||||
|
void InvalidateItemListViewState();
|
||||||
|
|
||||||
Windows::Foundation::Collections::IObservableVector<hstring> SearchMRU() { return m_searchMRUList; }
|
Windows::Foundation::Collections::IObservableVector<hstring> SearchMRU() { return m_searchMRUList; }
|
||||||
Windows::Foundation::Collections::IObservableVector<hstring> ReplaceMRU() { return m_replaceMRUList; }
|
Windows::Foundation::Collections::IObservableVector<hstring> ReplaceMRU() { return m_replaceMRUList; }
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::ExplorerItem> ExplorerItems() { return m_explorerItems; }
|
PowerRenameUI::ExplorerItemsSource ExplorerItems() { return m_explorerItems; }
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> SearchRegExShortcuts() { return m_searchRegExShortcuts; }
|
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> SearchRegExShortcuts() { return m_searchRegExShortcuts; }
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> DateTimeShortcuts() { return m_dateTimeShortcuts; }
|
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> DateTimeShortcuts() { return m_dateTimeShortcuts; }
|
||||||
hstring OriginalCount();
|
hstring OriginalCount();
|
||||||
@@ -87,27 +88,20 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
|
winrt::event_token PropertyChanged(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler);
|
||||||
void PropertyChanged(winrt::event_token const& token) noexcept;
|
void PropertyChanged(winrt::event_token const& token) noexcept;
|
||||||
|
|
||||||
void AddExplorerItem(int32_t id, hstring const& original, hstring const& renamed, int32_t type, uint32_t depth, bool checked);
|
|
||||||
void UpdateExplorerItem(int32_t id, std::optional<hstring> newOriginalName, std::optional<hstring> newName, PowerRenameItemRenameStatus itemStatus);
|
|
||||||
|
|
||||||
void SelectAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
void SelectAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||||
void ShowAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
void ShowAll(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||||
void ShowRenamed(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
void ShowRenamed(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::RoutedEventArgs const& e);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_allSelected;
|
bool m_allSelected;
|
||||||
inline PowerRenameUI::ExplorerItem FindById(int32_t id);
|
|
||||||
|
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<hstring> m_searchMRUList;
|
winrt::Windows::Foundation::Collections::IObservableVector<hstring> m_searchMRUList;
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<hstring> m_replaceMRUList;
|
winrt::Windows::Foundation::Collections::IObservableVector<hstring> m_replaceMRUList;
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::ExplorerItem> m_explorerItems;
|
PowerRenameUI::ExplorerItemsSource m_explorerItems;
|
||||||
std::map<int32_t, PowerRenameUI::ExplorerItem> m_explorerItemsMap;
|
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> m_searchRegExShortcuts;
|
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> m_searchRegExShortcuts;
|
||||||
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> m_dateTimeShortcuts;
|
winrt::Windows::Foundation::Collections::IObservableVector<PowerRenameUI::PatternSnippet> m_dateTimeShortcuts;
|
||||||
|
|
||||||
// Used by PowerRenameManagerEvents
|
// Used by PowerRenameManagerEvents
|
||||||
HRESULT OnItemAdded(_In_ IPowerRenameItem* renameItem);
|
|
||||||
HRESULT OnUpdate(_In_ IPowerRenameItem* renameItem);
|
|
||||||
HRESULT OnRename(_In_ IPowerRenameItem* renameItem);
|
HRESULT OnRename(_In_ IPowerRenameItem* renameItem);
|
||||||
HRESULT OnError(_In_ IPowerRenameItem*) { return S_OK; }
|
HRESULT OnError(_In_ IPowerRenameItem*) { return S_OK; }
|
||||||
HRESULT OnRegExStarted(_In_ DWORD) { return S_OK; }
|
HRESULT OnRegExStarted(_In_ DWORD) { return S_OK; }
|
||||||
@@ -124,7 +118,6 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
|
|
||||||
HRESULT CreateShellItemArrayFromPaths(std::vector<std::wstring> files, IShellItemArray** shellItemArray);
|
HRESULT CreateShellItemArrayFromPaths(std::vector<std::wstring> files, IShellItemArray** shellItemArray);
|
||||||
|
|
||||||
void PopulateExplorerItems();
|
|
||||||
HRESULT InitAutoComplete();
|
HRESULT InitAutoComplete();
|
||||||
HRESULT EnumerateShellItems(_In_ IEnumShellItems* enumShellItems);
|
HRESULT EnumerateShellItems(_In_ IEnumShellItems* enumShellItems);
|
||||||
void SearchReplaceChanged(bool forceRenaming = false);
|
void SearchReplaceChanged(bool forceRenaming = false);
|
||||||
@@ -155,6 +148,7 @@ namespace winrt::PowerRenameUI::implementation
|
|||||||
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
winrt::event<Microsoft::UI::Xaml::Data::PropertyChangedEventHandler> m_propertyChanged;
|
||||||
|
|
||||||
bool m_flagValidationInProgress = false;
|
bool m_flagValidationInProgress = false;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
void RegExItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
|
void RegExItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
|
||||||
void DateTimeItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
|
void DateTimeItemClick(winrt::Windows::Foundation::IInspectable const& sender, winrt::Microsoft::UI::Xaml::Controls::ItemClickEventArgs const& e);
|
||||||
|
|||||||
@@ -91,6 +91,14 @@
|
|||||||
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="ExplorerItemsSource.h">
|
||||||
|
<DependentUpon>ExplorerItemsSource.idl</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</ClInclude>
|
||||||
|
<ClInclude Include="ExplorerItemViewModel.h">
|
||||||
|
<DependentUpon>ExplorerItemViewModel.idl</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="PatternSnippet.h">
|
<ClInclude Include="PatternSnippet.h">
|
||||||
<DependentUpon>PatternSnippet.idl</DependentUpon>
|
<DependentUpon>PatternSnippet.idl</DependentUpon>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@@ -101,6 +109,7 @@
|
|||||||
<ClInclude Include="MainWindow.xaml.h">
|
<ClInclude Include="MainWindow.xaml.h">
|
||||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="Utils.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ApplicationDefinition Include="App.xaml" />
|
<ApplicationDefinition Include="App.xaml" />
|
||||||
@@ -114,6 +123,14 @@
|
|||||||
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
|
<ClCompile Include="ExplorerItemsSource.cpp">
|
||||||
|
<DependentUpon>ExplorerItemsSource.idl</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</ClCompile>
|
||||||
|
<ClCompile Include="ExplorerItemViewModel.cpp">
|
||||||
|
<DependentUpon>ExplorerItemViewModel.idl</DependentUpon>
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</ClCompile>
|
||||||
<ClCompile Include="PatternSnippet.cpp">
|
<ClCompile Include="PatternSnippet.cpp">
|
||||||
<DependentUpon>PatternSnippet.idl</DependentUpon>
|
<DependentUpon>PatternSnippet.idl</DependentUpon>
|
||||||
</ClCompile>
|
</ClCompile>
|
||||||
@@ -137,6 +154,12 @@
|
|||||||
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
<DependentUpon>ExplorerItem.xaml</DependentUpon>
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
</Midl>
|
</Midl>
|
||||||
|
<Midl Include="ExplorerItemsSource.idl">
|
||||||
|
<SubType>Code</SubType>
|
||||||
|
</Midl>
|
||||||
|
<Midl Include="ExplorerItemViewModel.idl">
|
||||||
|
<SubType>Designer</SubType>
|
||||||
|
</Midl>
|
||||||
<Midl Include="MainWindow.idl">
|
<Midl Include="MainWindow.idl">
|
||||||
<SubType>Code</SubType>
|
<SubType>Code</SubType>
|
||||||
<DependentUpon>MainWindow.xaml</DependentUpon>
|
<DependentUpon>MainWindow.xaml</DependentUpon>
|
||||||
|
|||||||
@@ -12,15 +12,19 @@
|
|||||||
<Midl Include="MainWindow.idl" />
|
<Midl Include="MainWindow.idl" />
|
||||||
<Midl Include="PatternSnippet.idl" />
|
<Midl Include="PatternSnippet.idl" />
|
||||||
<Midl Include="ExplorerItem.idl" />
|
<Midl Include="ExplorerItem.idl" />
|
||||||
|
<Midl Include="ExplorerItemsSource.idl" />
|
||||||
|
<Midl Include="ExplorerItemViewModel.idl" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClCompile Include="pch.cpp" />
|
<ClCompile Include="pch.cpp" />
|
||||||
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
<ClCompile Include="$(GeneratedFilesDir)module.g.cpp" />
|
||||||
<ClCompile Include="PatternSnippet.cpp" />
|
<ClCompile Include="PatternSnippet.cpp" />
|
||||||
|
<ClCompile Include="ExplorerItemsSource.cpp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
<ClInclude Include="PatternSnippet.h" />
|
<ClInclude Include="PatternSnippet.h" />
|
||||||
|
<ClInclude Include="ExplorerItemsSource.h" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Filter Include="Assets">
|
<Filter Include="Assets">
|
||||||
|
|||||||
25
src/modules/powerrename/PowerRenameUILib/Utils.h
Normal file
25
src/modules/powerrename/PowerRenameUILib/Utils.h
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
// Copyright (c) Microsoft Corporation.
|
||||||
|
// Licensed under the MIT license.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
// This macro defines a dependency property for a WinRT class.
|
||||||
|
// Use this in your class' header file after declaring it in the idl.
|
||||||
|
// Remember to register your dependency property in the respective cpp file.
|
||||||
|
#define DEPENDENCY_PROPERTY(type, name) \
|
||||||
|
public: \
|
||||||
|
static winrt::Microsoft::UI::Xaml::DependencyProperty name##Property() \
|
||||||
|
{ \
|
||||||
|
return _##name##Property; \
|
||||||
|
} \
|
||||||
|
type name() const \
|
||||||
|
{ \
|
||||||
|
return winrt::unbox_value<type>(GetValue(_##name##Property)); \
|
||||||
|
} \
|
||||||
|
void name(const type& value) \
|
||||||
|
{ \
|
||||||
|
SetValue(_##name##Property, winrt::box_value(value)); \
|
||||||
|
} \
|
||||||
|
\
|
||||||
|
private: \
|
||||||
|
static winrt::Microsoft::UI::Xaml::DependencyProperty _##name##Property;
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
#include <unknwn.h>
|
#include <unknwn.h>
|
||||||
#include <restrictederrorinfo.h>
|
#include <restrictederrorinfo.h>
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ void MRUListHandler::ParseJson()
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
std::vector<std::wstring> temp;
|
std::vector<std::wstring> temp;
|
||||||
for (unsigned int i = 0; i < min(jsonArray.Size(), size); ++i)
|
for (unsigned int i = 0; i < std::min(jsonArray.Size(), size); ++i)
|
||||||
{
|
{
|
||||||
int idx = (oldPushIdx + oldSize - (i + 1)) % oldSize;
|
int idx = (oldPushIdx + oldSize - (i + 1)) % oldSize;
|
||||||
temp.push_back(std::wstring(jsonArray.GetStringAt(idx)));
|
temp.push_back(std::wstring(jsonArray.GetStringAt(idx)));
|
||||||
|
|||||||
@@ -93,16 +93,16 @@ HRESULT CPowerRenameEnum::_ParseEnumItems(_In_ IEnumShellItems* pesi, _In_ int d
|
|||||||
|
|
||||||
while ((S_OK == pesi->Next(1, &spsi, &celtFetched)))
|
while ((S_OK == pesi->Next(1, &spsi, &celtFetched)))
|
||||||
{
|
{
|
||||||
items.push_back(spsi);
|
items.push_back(std::move(spsi));
|
||||||
spsi = nullptr;
|
spsi = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto cmpShellItems = [](CComPtr<IShellItem> l, CComPtr<IShellItem> r) {
|
auto cmpShellItems = [](const CComPtr<IShellItem>& l, const CComPtr<IShellItem>& r) {
|
||||||
int res = 0;
|
int res = 0;
|
||||||
l->Compare(r, SICHINT_DISPLAY, &res);
|
l->Compare(r, SICHINT_DISPLAY, &res);
|
||||||
return res < 0;
|
return res < 0;
|
||||||
};
|
};
|
||||||
std::sort(items.begin(), items.end(), cmpShellItems);
|
std::sort(begin(items), end(items), cmpShellItems);
|
||||||
|
|
||||||
for (const auto& item : items)
|
for (const auto& item : items)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,8 +95,6 @@ public:
|
|||||||
interface __declspec(uuid("87FC43F9-7634-43D9-99A5-20876AFCE4AD")) IPowerRenameManagerEvents : public IUnknown
|
interface __declspec(uuid("87FC43F9-7634-43D9-99A5-20876AFCE4AD")) IPowerRenameManagerEvents : public IUnknown
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
IFACEMETHOD(OnItemAdded)(_In_ IPowerRenameItem* renameItem) = 0;
|
|
||||||
IFACEMETHOD(OnUpdate)(_In_ IPowerRenameItem * renameItem) = 0;
|
|
||||||
IFACEMETHOD(OnRename)(_In_ IPowerRenameItem * renameItem) = 0;
|
IFACEMETHOD(OnRename)(_In_ IPowerRenameItem * renameItem) = 0;
|
||||||
IFACEMETHOD(OnError)(_In_ IPowerRenameItem * renameItem) = 0;
|
IFACEMETHOD(OnError)(_In_ IPowerRenameItem * renameItem) = 0;
|
||||||
IFACEMETHOD(OnRegExStarted)(_In_ DWORD threadId) = 0;
|
IFACEMETHOD(OnRegExStarted)(_In_ DWORD threadId) = 0;
|
||||||
@@ -135,6 +133,7 @@ public:
|
|||||||
IFACEMETHOD(PutRenameRegEx)(_In_ IPowerRenameRegEx* pRegEx) = 0;
|
IFACEMETHOD(PutRenameRegEx)(_In_ IPowerRenameRegEx* pRegEx) = 0;
|
||||||
IFACEMETHOD(GetRenameItemFactory)(_COM_Outptr_ IPowerRenameItemFactory** ppItemFactory) = 0;
|
IFACEMETHOD(GetRenameItemFactory)(_COM_Outptr_ IPowerRenameItemFactory** ppItemFactory) = 0;
|
||||||
IFACEMETHOD(PutRenameItemFactory)(_In_ IPowerRenameItemFactory* pItemFactory) = 0;
|
IFACEMETHOD(PutRenameItemFactory)(_In_ IPowerRenameItemFactory* pItemFactory) = 0;
|
||||||
|
virtual uint32_t GetVisibleItemRealIndex(const uint32_t index) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface __declspec(uuid("04AAFABE-B76E-4E13-993A-B5941F52B139")) IPowerRenameMRU : public IUnknown
|
interface __declspec(uuid("04AAFABE-B76E-4E13-993A-B5941F52B139")) IPowerRenameMRU : public IUnknown
|
||||||
|
|||||||
@@ -39,6 +39,7 @@
|
|||||||
<ClInclude Include="PowerRenameManager.h" />
|
<ClInclude Include="PowerRenameManager.h" />
|
||||||
<ClInclude Include="PowerRenameMRU.h" />
|
<ClInclude Include="PowerRenameMRU.h" />
|
||||||
<ClInclude Include="PowerRenameRegEx.h" />
|
<ClInclude Include="PowerRenameRegEx.h" />
|
||||||
|
<ClInclude Include="Renaming.h" />
|
||||||
<ClInclude Include="Settings.h" />
|
<ClInclude Include="Settings.h" />
|
||||||
<ClInclude Include="srwlock.h" />
|
<ClInclude Include="srwlock.h" />
|
||||||
<ClInclude Include="pch.h" />
|
<ClInclude Include="pch.h" />
|
||||||
@@ -53,6 +54,7 @@
|
|||||||
<ClCompile Include="PowerRenameManager.cpp" />
|
<ClCompile Include="PowerRenameManager.cpp" />
|
||||||
<ClCompile Include="PowerRenameMRU.cpp" />
|
<ClCompile Include="PowerRenameMRU.cpp" />
|
||||||
<ClCompile Include="PowerRenameRegEx.cpp" />
|
<ClCompile Include="PowerRenameRegEx.cpp" />
|
||||||
|
<ClCompile Include="Renaming.cpp" />
|
||||||
<ClCompile Include="Settings.cpp" />
|
<ClCompile Include="Settings.cpp" />
|
||||||
<ClCompile Include="pch.cpp">
|
<ClCompile Include="pch.cpp">
|
||||||
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
<PrecompiledHeader Condition="'$(CIBuild)'!='true'">Create</PrecompiledHeader>
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
#include <shlobj.h>
|
#include <shlobj.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include <filesystem>
|
|
||||||
#include "trace.h"
|
#include "trace.h"
|
||||||
#include <winrt/base.h>
|
#include <Renaming.h>
|
||||||
|
|
||||||
namespace fs = std::filesystem;
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
@@ -174,11 +173,6 @@ IFACEMETHODIMP CPowerRenameManager::AddItem(_In_ IPowerRenameItem* pItem)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (SUCCEEDED(hr))
|
|
||||||
{
|
|
||||||
_OnItemAdded(pItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,25 +206,32 @@ IFACEMETHODIMP CPowerRenameManager::GetVisibleItemByIndex(_In_ UINT index, _COM_
|
|||||||
}
|
}
|
||||||
else if (SUCCEEDED(GetVisibleItemCount(&count)) && index < count)
|
else if (SUCCEEDED(GetVisibleItemCount(&count)) && index < count)
|
||||||
{
|
{
|
||||||
UINT realIndex = 0, visibleIndex = 0;
|
const UINT realIndex = GetVisibleItemRealIndex(index);
|
||||||
for (size_t i = 0; i < m_isVisible.size(); i++)
|
|
||||||
{
|
|
||||||
if (m_isVisible[i] && visibleIndex == index)
|
|
||||||
{
|
|
||||||
realIndex = static_cast<UINT>(i);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (m_isVisible[i])
|
|
||||||
{
|
|
||||||
visibleIndex++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hr = GetItemByIndex(realIndex, ppItem);
|
hr = GetItemByIndex(realIndex, ppItem);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hr;
|
return hr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint32_t CPowerRenameManager::GetVisibleItemRealIndex(const uint32_t index) const
|
||||||
|
{
|
||||||
|
UINT realIndex = 0, visibleIndex = 0;
|
||||||
|
for (size_t i = 0; i < m_isVisible.size(); i++)
|
||||||
|
{
|
||||||
|
if (m_isVisible[i] && visibleIndex == index)
|
||||||
|
{
|
||||||
|
realIndex = static_cast<UINT>(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (m_isVisible[i])
|
||||||
|
{
|
||||||
|
visibleIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return realIndex;
|
||||||
|
}
|
||||||
|
|
||||||
IFACEMETHODIMP CPowerRenameManager::GetItemById(_In_ int id, _COM_Outptr_ IPowerRenameItem** ppItem)
|
IFACEMETHODIMP CPowerRenameManager::GetItemById(_In_ int id, _COM_Outptr_ IPowerRenameItem** ppItem)
|
||||||
{
|
{
|
||||||
*ppItem = nullptr;
|
*ppItem = nullptr;
|
||||||
@@ -464,7 +465,7 @@ IFACEMETHODIMP CPowerRenameManager::OnFileTimeChanged(_In_ SYSTEMTIME /*fileTime
|
|||||||
HRESULT CPowerRenameManager::s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm)
|
HRESULT CPowerRenameManager::s_CreateInstance(_Outptr_ IPowerRenameManager** ppsrm)
|
||||||
{
|
{
|
||||||
*ppsrm = nullptr;
|
*ppsrm = nullptr;
|
||||||
CPowerRenameManager *psrm = new CPowerRenameManager();
|
CPowerRenameManager* psrm = new CPowerRenameManager();
|
||||||
HRESULT hr = E_OUTOFMEMORY;
|
HRESULT hr = E_OUTOFMEMORY;
|
||||||
if (psrm)
|
if (psrm)
|
||||||
{
|
{
|
||||||
@@ -554,12 +555,7 @@ LRESULT CPowerRenameManager::_WndProc(_In_ HWND hwnd, _In_ UINT msg, _In_ WPARAM
|
|||||||
{
|
{
|
||||||
case SRM_REGEX_ITEM_UPDATED:
|
case SRM_REGEX_ITEM_UPDATED:
|
||||||
{
|
{
|
||||||
int id = static_cast<int>(lParam);
|
// Do nothing.
|
||||||
CComPtr<IPowerRenameItem> spItem;
|
|
||||||
if (SUCCEEDED(GetItemById(id, &spItem)))
|
|
||||||
{
|
|
||||||
_OnUpdate(spItem);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case SRM_REGEX_ITEM_RENAMED_KEEP_UI:
|
case SRM_REGEX_ITEM_RENAMED_KEEP_UI:
|
||||||
@@ -926,22 +922,10 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
|
|||||||
|
|
||||||
winrt::check_hresult(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx));
|
winrt::check_hresult(pwtd->spsrm->GetRenameRegEx(&spRenameRegEx));
|
||||||
|
|
||||||
DWORD flags = 0;
|
|
||||||
winrt::check_hresult(spRenameRegEx->GetFlags(&flags));
|
|
||||||
|
|
||||||
PWSTR replaceTerm = nullptr;
|
|
||||||
bool useFileTime = false;
|
|
||||||
|
|
||||||
winrt::check_hresult(spRenameRegEx->GetReplaceTerm(&replaceTerm));
|
|
||||||
|
|
||||||
if (isFileTimeUsed(replaceTerm))
|
|
||||||
{
|
|
||||||
useFileTime = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
UINT itemCount = 0;
|
UINT itemCount = 0;
|
||||||
unsigned long itemEnumIndex = 1;
|
unsigned long itemEnumIndex = 1;
|
||||||
winrt::check_hresult(pwtd->spsrm->GetItemCount(&itemCount));
|
winrt::check_hresult(pwtd->spsrm->GetItemCount(&itemCount));
|
||||||
|
|
||||||
for (UINT u = 0; u < itemCount; u++)
|
for (UINT u = 0; u < itemCount; u++)
|
||||||
{
|
{
|
||||||
// Check if cancel event is signaled
|
// Check if cancel event is signaled
|
||||||
@@ -956,215 +940,14 @@ DWORD WINAPI CPowerRenameManager::s_regexWorkerThread(_In_ void* pv)
|
|||||||
CComPtr<IPowerRenameItem> spItem;
|
CComPtr<IPowerRenameItem> spItem;
|
||||||
winrt::check_hresult(pwtd->spsrm->GetItemByIndex(u, &spItem));
|
winrt::check_hresult(pwtd->spsrm->GetItemByIndex(u, &spItem));
|
||||||
|
|
||||||
int id = -1;
|
DoRename(spRenameRegEx, itemEnumIndex, spItem);
|
||||||
winrt::check_hresult(spItem->GetId(&id));
|
|
||||||
|
|
||||||
bool isFolder = false;
|
|
||||||
bool isSubFolderContent = false;
|
|
||||||
winrt::check_hresult(spItem->GetIsFolder(&isFolder));
|
|
||||||
winrt::check_hresult(spItem->GetIsSubFolderContent(&isSubFolderContent));
|
|
||||||
if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
|
|
||||||
(!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
|
|
||||||
(isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)) ||
|
|
||||||
(isFolder && (flags & PowerRenameFlags::ExtensionOnly)))
|
|
||||||
{
|
|
||||||
// Exclude this item from renaming. Ensure new name is cleared.
|
|
||||||
winrt::check_hresult(spItem->PutNewName(nullptr));
|
|
||||||
|
|
||||||
// Send the manager thread the item processed message
|
|
||||||
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PWSTR originalName = nullptr;
|
|
||||||
winrt::check_hresult(spItem->GetOriginalName(&originalName));
|
|
||||||
|
|
||||||
|
|
||||||
PWSTR currentNewName = nullptr;
|
|
||||||
winrt::check_hresult(spItem->GetNewName(¤tNewName));
|
|
||||||
|
|
||||||
wchar_t sourceName[MAX_PATH] = { 0 };
|
|
||||||
|
|
||||||
if (isFolder)
|
|
||||||
{
|
|
||||||
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flags & NameOnly)
|
|
||||||
{
|
|
||||||
StringCchCopy(sourceName, ARRAYSIZE(sourceName), fs::path(originalName).stem().c_str());
|
|
||||||
}
|
|
||||||
else if (flags & ExtensionOnly)
|
|
||||||
{
|
|
||||||
std::wstring extension = fs::path(originalName).extension().wstring();
|
|
||||||
if (!extension.empty() && extension.front() == '.')
|
|
||||||
{
|
|
||||||
extension = extension.erase(0, 1);
|
|
||||||
}
|
|
||||||
StringCchCopy(sourceName, ARRAYSIZE(sourceName), extension.c_str());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SYSTEMTIME fileTime = { 0 };
|
|
||||||
|
|
||||||
if (useFileTime)
|
|
||||||
{
|
|
||||||
winrt::check_hresult(spItem->GetTime(&fileTime));
|
|
||||||
winrt::check_hresult(spRenameRegEx->PutFileTime(fileTime));
|
|
||||||
}
|
|
||||||
|
|
||||||
PWSTR newName = nullptr;
|
|
||||||
|
|
||||||
// Failure here means we didn't match anything or had nothing to match
|
|
||||||
// Call put_newName with null in that case to reset it
|
|
||||||
winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName));
|
|
||||||
|
|
||||||
if (useFileTime)
|
|
||||||
{
|
|
||||||
winrt::check_hresult(spRenameRegEx->ResetFileTime());
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t resultName[MAX_PATH] = { 0 };
|
|
||||||
|
|
||||||
PWSTR newNameToUse = nullptr;
|
|
||||||
|
|
||||||
// newName == nullptr likely means we have an empty search string. We should leave newNameToUse
|
|
||||||
// as nullptr so we clear the renamed column
|
|
||||||
// Except string transformation is selected.
|
|
||||||
|
|
||||||
if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
|
|
||||||
{
|
|
||||||
SHStrDup(sourceName, &newName);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (newName != nullptr)
|
|
||||||
{
|
|
||||||
newNameToUse = resultName;
|
|
||||||
|
|
||||||
if (isFolder)
|
|
||||||
{
|
|
||||||
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flags & NameOnly)
|
|
||||||
{
|
|
||||||
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s%s", newName, fs::path(originalName).extension().c_str());
|
|
||||||
}
|
|
||||||
else if (flags & ExtensionOnly)
|
|
||||||
{
|
|
||||||
std::wstring extension = fs::path(originalName).extension().wstring();
|
|
||||||
if (!extension.empty())
|
|
||||||
{
|
|
||||||
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s.%s", fs::path(originalName).stem().c_str(), newName);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringCchCopy(resultName, ARRAYSIZE(resultName), originalName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t trimmedName[MAX_PATH] = { 0 };
|
|
||||||
if (newNameToUse != nullptr)
|
|
||||||
{
|
|
||||||
winrt::check_hresult(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse));
|
|
||||||
newNameToUse = trimmedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t transformedName[MAX_PATH] = { 0 };
|
|
||||||
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
winrt::check_hresult(GetTransformedFileName(transformedName, ARRAYSIZE(transformedName), newNameToUse, flags, isFolder));
|
|
||||||
}
|
|
||||||
catch (...)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
newNameToUse = transformedName;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No change from originalName so set newName to
|
|
||||||
// null so we clear it from our UI as well.
|
|
||||||
if (lstrcmp(originalName, newNameToUse) == 0)
|
|
||||||
{
|
|
||||||
newNameToUse = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
wchar_t uniqueName[MAX_PATH] = { 0 };
|
|
||||||
if (newNameToUse != nullptr && (flags & EnumerateItems))
|
|
||||||
{
|
|
||||||
unsigned long countUsed = 0;
|
|
||||||
if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed))
|
|
||||||
{
|
|
||||||
newNameToUse = uniqueName;
|
|
||||||
}
|
|
||||||
itemEnumIndex++;
|
|
||||||
}
|
|
||||||
|
|
||||||
spItem->PutStatus(PowerRenameItemRenameStatus::ShouldRename);
|
|
||||||
if (newNameToUse != nullptr)
|
|
||||||
{
|
|
||||||
std::wstring newNameToUseWstr{ newNameToUse };
|
|
||||||
PWSTR path = nullptr;
|
|
||||||
spItem->GetPath(&path);
|
|
||||||
|
|
||||||
// Following characters cannot be used for file names.
|
|
||||||
// Ref https://learn.microsoft.com/windows/win32/fileio/naming-a-file#naming-conventions
|
|
||||||
if (newNameToUseWstr.contains('<') ||
|
|
||||||
newNameToUseWstr.contains('>') ||
|
|
||||||
newNameToUseWstr.contains(':') ||
|
|
||||||
newNameToUseWstr.contains('"') ||
|
|
||||||
newNameToUseWstr.contains('\\') ||
|
|
||||||
newNameToUseWstr.contains('/') ||
|
|
||||||
newNameToUseWstr.contains('|') ||
|
|
||||||
newNameToUseWstr.contains('?') ||
|
|
||||||
newNameToUseWstr.contains('*'))
|
|
||||||
{
|
|
||||||
spItem->PutStatus(PowerRenameItemRenameStatus::ItemNameInvalidChar);
|
|
||||||
}
|
|
||||||
// Max file path is 260 and max folder path is 247.
|
|
||||||
// Ref https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
|
|
||||||
else if ((isFolder && lstrlen(path) + (lstrlen(newNameToUse) - lstrlen(originalName)) > 247) ||
|
|
||||||
lstrlen(path) + (lstrlen(newNameToUse) - lstrlen(originalName)) > 260)
|
|
||||||
{
|
|
||||||
spItem->PutStatus(PowerRenameItemRenameStatus::ItemNameTooLong);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
winrt::check_hresult(spItem->PutNewName(newNameToUse));
|
|
||||||
|
|
||||||
// Was there a change?
|
|
||||||
if (lstrcmp(currentNewName, newNameToUse) != 0)
|
|
||||||
{
|
|
||||||
// Send the manager thread the item processed message
|
|
||||||
PostMessage(pwtd->hwndManager, SRM_REGEX_ITEM_UPDATED, GetCurrentThreadId(), id);
|
|
||||||
}
|
|
||||||
CoTaskMemFree(newName);
|
|
||||||
CoTaskMemFree(currentNewName);
|
|
||||||
CoTaskMemFree(originalName);
|
|
||||||
}
|
}
|
||||||
CoTaskMemFree(replaceTerm);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the manager thread the completion message
|
// Send the manager thread the completion message
|
||||||
PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0);
|
PostMessage(pwtd->hwndManager, SRM_REGEX_COMPLETE, GetCurrentThreadId(), 0);
|
||||||
|
|
||||||
delete pwtd;
|
delete pwtd;
|
||||||
|
|
||||||
}
|
}
|
||||||
CoUninitialize();
|
CoUninitialize();
|
||||||
}
|
}
|
||||||
@@ -1249,32 +1032,6 @@ void CPowerRenameManager::_ClearRegEx()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPowerRenameManager::_OnItemAdded(_In_ IPowerRenameItem* renameItem)
|
|
||||||
{
|
|
||||||
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
||||||
|
|
||||||
for (auto it : m_powerRenameManagerEvents)
|
|
||||||
{
|
|
||||||
if (it.pEvents)
|
|
||||||
{
|
|
||||||
it.pEvents->OnItemAdded(renameItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPowerRenameManager::_OnUpdate(_In_ IPowerRenameItem* renameItem)
|
|
||||||
{
|
|
||||||
CSRWSharedAutoLock lock(&m_lockEvents);
|
|
||||||
|
|
||||||
for (auto it : m_powerRenameManagerEvents)
|
|
||||||
{
|
|
||||||
if (it.pEvents)
|
|
||||||
{
|
|
||||||
it.pEvents->OnUpdate(renameItem);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void CPowerRenameManager::_OnRename(_In_ IPowerRenameItem* renameItem)
|
void CPowerRenameManager::_OnRename(_In_ IPowerRenameItem* renameItem)
|
||||||
{
|
{
|
||||||
CSRWSharedAutoLock lock(&m_lockEvents);
|
CSRWSharedAutoLock lock(&m_lockEvents);
|
||||||
|
|||||||
@@ -11,12 +11,12 @@ class CPowerRenameManager :
|
|||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
// IUnknown
|
// IUnknown
|
||||||
IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface);
|
IFACEMETHODIMP QueryInterface(_In_ REFIID iid, _Outptr_ void** resultInterface);
|
||||||
IFACEMETHODIMP_(ULONG) AddRef();
|
IFACEMETHODIMP_(ULONG) AddRef();
|
||||||
IFACEMETHODIMP_(ULONG) Release();
|
IFACEMETHODIMP_(ULONG) Release();
|
||||||
|
|
||||||
// IPowerRenameManager
|
// IPowerRenameManager
|
||||||
IFACEMETHODIMP Advise(_In_ IPowerRenameManagerEvents* renameOpEvent, _Out_ DWORD *cookie);
|
IFACEMETHODIMP Advise(_In_ IPowerRenameManagerEvents* renameOpEvent, _Out_ DWORD* cookie);
|
||||||
IFACEMETHODIMP UnAdvise(_In_ DWORD cookie);
|
IFACEMETHODIMP UnAdvise(_In_ DWORD cookie);
|
||||||
IFACEMETHODIMP Start();
|
IFACEMETHODIMP Start();
|
||||||
IFACEMETHODIMP Stop();
|
IFACEMETHODIMP Stop();
|
||||||
@@ -42,7 +42,9 @@ public:
|
|||||||
IFACEMETHODIMP PutRenameRegEx(_In_ IPowerRenameRegEx* pRegEx);
|
IFACEMETHODIMP PutRenameRegEx(_In_ IPowerRenameRegEx* pRegEx);
|
||||||
IFACEMETHODIMP GetRenameItemFactory(_COM_Outptr_ IPowerRenameItemFactory** ppItemFactory);
|
IFACEMETHODIMP GetRenameItemFactory(_COM_Outptr_ IPowerRenameItemFactory** ppItemFactory);
|
||||||
IFACEMETHODIMP PutRenameItemFactory(_In_ IPowerRenameItemFactory* pItemFactory);
|
IFACEMETHODIMP PutRenameItemFactory(_In_ IPowerRenameItemFactory* pItemFactory);
|
||||||
|
|
||||||
|
uint32_t GetVisibleItemRealIndex(const uint32_t index) const override;
|
||||||
|
|
||||||
// IPowerRenameRegExEvents
|
// IPowerRenameRegExEvents
|
||||||
IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm);
|
IFACEMETHODIMP OnSearchTermChanged(_In_ PCWSTR searchTerm);
|
||||||
IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm);
|
IFACEMETHODIMP OnReplaceTermChanged(_In_ PCWSTR replaceTerm);
|
||||||
@@ -60,8 +62,6 @@ protected:
|
|||||||
|
|
||||||
void _Cancel();
|
void _Cancel();
|
||||||
|
|
||||||
void _OnItemAdded(_In_ IPowerRenameItem* renameItem);
|
|
||||||
void _OnUpdate(_In_ IPowerRenameItem* renameItem);
|
|
||||||
void _OnRename(_In_ IPowerRenameItem* renameItem);
|
void _OnRename(_In_ IPowerRenameItem* renameItem);
|
||||||
void _OnError(_In_ IPowerRenameItem* renameItem);
|
void _OnError(_In_ IPowerRenameItem* renameItem);
|
||||||
void _OnRegExStarted(_In_ DWORD threadId);
|
void _OnRegExStarted(_In_ DWORD threadId);
|
||||||
|
|||||||
220
src/modules/powerrename/lib/Renaming.cpp
Normal file
220
src/modules/powerrename/lib/Renaming.cpp
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
#include "pch.h"
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|
||||||
|
#include "Renaming.h"
|
||||||
|
#include <Helpers.h>
|
||||||
|
|
||||||
|
namespace fs = std::filesystem;
|
||||||
|
|
||||||
|
bool DoRename(CComPtr<IPowerRenameRegEx>& spRenameRegEx, unsigned long& itemEnumIndex, CComPtr<IPowerRenameItem>& spItem)
|
||||||
|
{
|
||||||
|
bool wouldRename = false;
|
||||||
|
DWORD flags = 0;
|
||||||
|
winrt::check_hresult(spRenameRegEx->GetFlags(&flags));
|
||||||
|
|
||||||
|
PWSTR replaceTerm = nullptr;
|
||||||
|
bool useFileTime = false;
|
||||||
|
|
||||||
|
winrt::check_hresult(spRenameRegEx->GetReplaceTerm(&replaceTerm));
|
||||||
|
|
||||||
|
if (isFileTimeUsed(replaceTerm))
|
||||||
|
{
|
||||||
|
useFileTime = true;
|
||||||
|
}
|
||||||
|
CoTaskMemFree(replaceTerm);
|
||||||
|
|
||||||
|
int id = -1;
|
||||||
|
winrt::check_hresult(spItem->GetId(&id));
|
||||||
|
|
||||||
|
bool isFolder = false;
|
||||||
|
bool isSubFolderContent = false;
|
||||||
|
winrt::check_hresult(spItem->GetIsFolder(&isFolder));
|
||||||
|
winrt::check_hresult(spItem->GetIsSubFolderContent(&isSubFolderContent));
|
||||||
|
if ((isFolder && (flags & PowerRenameFlags::ExcludeFolders)) ||
|
||||||
|
(!isFolder && (flags & PowerRenameFlags::ExcludeFiles)) ||
|
||||||
|
(isSubFolderContent && (flags & PowerRenameFlags::ExcludeSubfolders)) ||
|
||||||
|
(isFolder && (flags & PowerRenameFlags::ExtensionOnly)))
|
||||||
|
{
|
||||||
|
// Exclude this item from renaming. Ensure new name is cleared.
|
||||||
|
winrt::check_hresult(spItem->PutNewName(nullptr));
|
||||||
|
|
||||||
|
return wouldRename;
|
||||||
|
}
|
||||||
|
|
||||||
|
PWSTR originalName = nullptr;
|
||||||
|
winrt::check_hresult(spItem->GetOriginalName(&originalName));
|
||||||
|
|
||||||
|
PWSTR currentNewName = nullptr;
|
||||||
|
winrt::check_hresult(spItem->GetNewName(¤tNewName));
|
||||||
|
|
||||||
|
wchar_t sourceName[MAX_PATH] = { 0 };
|
||||||
|
|
||||||
|
if (isFolder)
|
||||||
|
{
|
||||||
|
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (flags & NameOnly)
|
||||||
|
{
|
||||||
|
StringCchCopy(sourceName, ARRAYSIZE(sourceName), fs::path(originalName).stem().c_str());
|
||||||
|
}
|
||||||
|
else if (flags & ExtensionOnly)
|
||||||
|
{
|
||||||
|
std::wstring extension = fs::path(originalName).extension().wstring();
|
||||||
|
if (!extension.empty() && extension.front() == '.')
|
||||||
|
{
|
||||||
|
extension = extension.erase(0, 1);
|
||||||
|
}
|
||||||
|
StringCchCopy(sourceName, ARRAYSIZE(sourceName), extension.c_str());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringCchCopy(sourceName, ARRAYSIZE(sourceName), originalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SYSTEMTIME fileTime = { 0 };
|
||||||
|
|
||||||
|
if (useFileTime)
|
||||||
|
{
|
||||||
|
winrt::check_hresult(spItem->GetTime(&fileTime));
|
||||||
|
winrt::check_hresult(spRenameRegEx->PutFileTime(fileTime));
|
||||||
|
}
|
||||||
|
|
||||||
|
PWSTR newName = nullptr;
|
||||||
|
|
||||||
|
// Failure here means we didn't match anything or had nothing to match
|
||||||
|
// Call put_newName with null in that case to reset it
|
||||||
|
winrt::check_hresult(spRenameRegEx->Replace(sourceName, &newName));
|
||||||
|
|
||||||
|
if (useFileTime)
|
||||||
|
{
|
||||||
|
winrt::check_hresult(spRenameRegEx->ResetFileTime());
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t resultName[MAX_PATH] = { 0 };
|
||||||
|
|
||||||
|
PWSTR newNameToUse = nullptr;
|
||||||
|
|
||||||
|
// newName == nullptr likely means we have an empty search string. We should leave newNameToUse
|
||||||
|
// as nullptr so we clear the renamed column
|
||||||
|
// Except string transformation is selected.
|
||||||
|
|
||||||
|
if (newName == nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
|
||||||
|
{
|
||||||
|
SHStrDup(sourceName, &newName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newName != nullptr)
|
||||||
|
{
|
||||||
|
newNameToUse = resultName;
|
||||||
|
|
||||||
|
if (isFolder)
|
||||||
|
{
|
||||||
|
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (flags & NameOnly)
|
||||||
|
{
|
||||||
|
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s%s", newName, fs::path(originalName).extension().c_str());
|
||||||
|
}
|
||||||
|
else if (flags & ExtensionOnly)
|
||||||
|
{
|
||||||
|
std::wstring extension = fs::path(originalName).extension().wstring();
|
||||||
|
if (!extension.empty())
|
||||||
|
{
|
||||||
|
StringCchPrintf(resultName, ARRAYSIZE(resultName), L"%s.%s", fs::path(originalName).stem().c_str(), newName);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringCchCopy(resultName, ARRAYSIZE(resultName), originalName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
StringCchCopy(resultName, ARRAYSIZE(resultName), newName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t trimmedName[MAX_PATH] = { 0 };
|
||||||
|
if (newNameToUse != nullptr)
|
||||||
|
{
|
||||||
|
winrt::check_hresult(GetTrimmedFileName(trimmedName, ARRAYSIZE(trimmedName), newNameToUse));
|
||||||
|
newNameToUse = trimmedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t transformedName[MAX_PATH] = { 0 };
|
||||||
|
if (newNameToUse != nullptr && (flags & Uppercase || flags & Lowercase || flags & Titlecase || flags & Capitalized))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
winrt::check_hresult(GetTransformedFileName(transformedName, ARRAYSIZE(transformedName), newNameToUse, flags, isFolder));
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
newNameToUse = transformedName;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No change from originalName so set newName to
|
||||||
|
// null so we clear it from our UI as well.
|
||||||
|
if (lstrcmp(originalName, newNameToUse) == 0)
|
||||||
|
{
|
||||||
|
newNameToUse = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
wchar_t uniqueName[MAX_PATH] = { 0 };
|
||||||
|
if (newNameToUse != nullptr && (flags & EnumerateItems))
|
||||||
|
{
|
||||||
|
unsigned long countUsed = 0;
|
||||||
|
if (GetEnumeratedFileName(uniqueName, ARRAYSIZE(uniqueName), newNameToUse, nullptr, itemEnumIndex, &countUsed))
|
||||||
|
{
|
||||||
|
newNameToUse = uniqueName;
|
||||||
|
}
|
||||||
|
itemEnumIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
spItem->PutStatus(PowerRenameItemRenameStatus::ShouldRename);
|
||||||
|
if (newNameToUse != nullptr)
|
||||||
|
{
|
||||||
|
wouldRename = true;
|
||||||
|
std::wstring newNameToUseWstr{ newNameToUse };
|
||||||
|
PWSTR path = nullptr;
|
||||||
|
spItem->GetPath(&path);
|
||||||
|
|
||||||
|
// Following characters cannot be used for file names.
|
||||||
|
// Ref https://learn.microsoft.com/windows/win32/fileio/naming-a-file#naming-conventions
|
||||||
|
if (newNameToUseWstr.contains('<') ||
|
||||||
|
newNameToUseWstr.contains('>') ||
|
||||||
|
newNameToUseWstr.contains(':') ||
|
||||||
|
newNameToUseWstr.contains('"') ||
|
||||||
|
newNameToUseWstr.contains('\\') ||
|
||||||
|
newNameToUseWstr.contains('/') ||
|
||||||
|
newNameToUseWstr.contains('|') ||
|
||||||
|
newNameToUseWstr.contains('?') ||
|
||||||
|
newNameToUseWstr.contains('*'))
|
||||||
|
{
|
||||||
|
spItem->PutStatus(PowerRenameItemRenameStatus::ItemNameInvalidChar);
|
||||||
|
wouldRename = false;
|
||||||
|
}
|
||||||
|
// Max file path is 260 and max folder path is 247.
|
||||||
|
// Ref https://learn.microsoft.com/windows/win32/fileio/maximum-file-path-limitation?tabs=registry
|
||||||
|
else if ((isFolder && lstrlen(path) + (lstrlen(newNameToUse) - lstrlen(originalName)) > 247) ||
|
||||||
|
lstrlen(path) + (lstrlen(newNameToUse) - lstrlen(originalName)) > 260)
|
||||||
|
{
|
||||||
|
spItem->PutStatus(PowerRenameItemRenameStatus::ItemNameTooLong);
|
||||||
|
wouldRename = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
winrt::check_hresult(spItem->PutNewName(newNameToUse));
|
||||||
|
|
||||||
|
CoTaskMemFree(newName);
|
||||||
|
CoTaskMemFree(currentNewName);
|
||||||
|
CoTaskMemFree(originalName);
|
||||||
|
|
||||||
|
return wouldRename;
|
||||||
|
}
|
||||||
5
src/modules/powerrename/lib/Renaming.h
Normal file
5
src/modules/powerrename/lib/Renaming.h
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <PowerRenameInterfaces.h>
|
||||||
|
|
||||||
|
bool DoRename(CComPtr<IPowerRenameRegEx>& spRenameRegEx, unsigned long& itemEnumIndex, CComPtr<IPowerRenameItem>& spItem);
|
||||||
@@ -2,12 +2,13 @@
|
|||||||
|
|
||||||
#include "targetver.h"
|
#include "targetver.h"
|
||||||
|
|
||||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||||
|
#define NOMINMAX
|
||||||
// Windows Header Files:
|
// Windows Header Files:
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
|
|
||||||
// C RunTime Header Files
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <execution>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <malloc.h>
|
#include <malloc.h>
|
||||||
#include <memory.h>
|
#include <memory.h>
|
||||||
@@ -19,6 +20,8 @@
|
|||||||
#include <shellapi.h>
|
#include <shellapi.h>
|
||||||
#include <shlwapi.h>
|
#include <shlwapi.h>
|
||||||
#include <ShlObj_core.h>
|
#include <ShlObj_core.h>
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
#include <ProjectTelemetry.h>
|
#include <ProjectTelemetry.h>
|
||||||
|
|
||||||
|
#include <winrt/base.h>
|
||||||
|
|||||||
@@ -29,18 +29,6 @@ CMockPowerRenameManagerEvents::Release()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// IPowerRenameManagerEvents
|
// IPowerRenameManagerEvents
|
||||||
IFACEMETHODIMP CMockPowerRenameManagerEvents::OnItemAdded(_In_ IPowerRenameItem* pItem)
|
|
||||||
{
|
|
||||||
m_itemAdded = pItem;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFACEMETHODIMP CMockPowerRenameManagerEvents::OnUpdate(_In_ IPowerRenameItem* pItem)
|
|
||||||
{
|
|
||||||
m_itemUpdated = pItem;
|
|
||||||
return S_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
IFACEMETHODIMP CMockPowerRenameManagerEvents::OnRename(_In_ IPowerRenameItem* pItem)
|
IFACEMETHODIMP CMockPowerRenameManagerEvents::OnRename(_In_ IPowerRenameItem* pItem)
|
||||||
{
|
{
|
||||||
m_itemRenamed = pItem;
|
m_itemRenamed = pItem;
|
||||||
|
|||||||
@@ -32,8 +32,6 @@ public:
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
CComPtr<IPowerRenameItem> m_itemAdded;
|
|
||||||
CComPtr<IPowerRenameItem> m_itemUpdated;
|
|
||||||
CComPtr<IPowerRenameItem> m_itemRenamed;
|
CComPtr<IPowerRenameItem> m_itemRenamed;
|
||||||
CComPtr<IPowerRenameItem> m_itemError;
|
CComPtr<IPowerRenameItem> m_itemError;
|
||||||
bool m_regExStarted = false;
|
bool m_regExStarted = false;
|
||||||
|
|||||||
@@ -68,12 +68,6 @@ namespace PowerRenameManagerTests
|
|||||||
int itemId = 0;
|
int itemId = 0;
|
||||||
Assert::IsTrue(item->GetId(&itemId) == S_OK);
|
Assert::IsTrue(item->GetId(&itemId) == S_OK);
|
||||||
mgr->AddItem(item);
|
mgr->AddItem(item);
|
||||||
|
|
||||||
// Verify the item we added is the same from the event
|
|
||||||
Assert::IsTrue(mockMgrEvents->m_itemAdded != nullptr && mockMgrEvents->m_itemAdded == item);
|
|
||||||
int eventItemId = 0;
|
|
||||||
Assert::IsTrue(mockMgrEvents->m_itemAdded->GetId(&eventItemId) == S_OK);
|
|
||||||
Assert::IsTrue(itemId == eventItemId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Setup match and replace parameters
|
// TODO: Setup match and replace parameters
|
||||||
@@ -146,11 +140,6 @@ namespace PowerRenameManagerTests
|
|||||||
Assert::IsTrue(item->GetId(&itemId) == S_OK);
|
Assert::IsTrue(item->GetId(&itemId) == S_OK);
|
||||||
mgr->AddItem(item);
|
mgr->AddItem(item);
|
||||||
|
|
||||||
// Verify the item we added is the same from the event
|
|
||||||
Assert::IsTrue(mockMgrEvents->m_itemAdded != nullptr && mockMgrEvents->m_itemAdded == item);
|
|
||||||
int eventItemId = 0;
|
|
||||||
Assert::IsTrue(mockMgrEvents->m_itemAdded->GetId(&eventItemId) == S_OK);
|
|
||||||
Assert::IsTrue(itemId == eventItemId);
|
|
||||||
Assert::IsTrue(mgr->Shutdown() == S_OK);
|
Assert::IsTrue(mgr->Shutdown() == S_OK);
|
||||||
|
|
||||||
mockMgrEvents->Release();
|
mockMgrEvents->Release();
|
||||||
|
|||||||
Reference in New Issue
Block a user