This commit is contained in:
Clint Rutkas 2019-11-11 12:46:06 -08:00
commit 1268a463ac
47 changed files with 731 additions and 356 deletions

View File

@ -6,11 +6,11 @@
<!-- Please review the items on the PR checklist before submitting-->
## PR Checklist
* [ ] Closes #xxx
* [ ] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/PowerToys) and sign the CLA
* [ ] Tests added/passed
* [ ] Requires documentation to be updated
* [ ] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
* [] Applies to #xxx
* [] CLA signed. If not, go over [here](https://cla.opensource.microsoft.com/microsoft/PowerToys) and sign the CLA
* [] Tests added/passed
* [] Requires documentation to be updated
* [] I've discussed this with core contributors already. If not checked, I'm ready to accept this work might be rejected in favor of a different grand plan. Issue number where discussion took place: #xxx
<!-- Provide a more detailed description of the PR, other things fixed or any additional comments/features here -->
## Detailed Description of the Pull Request / Additional comments

BIN
Logo.jpg

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.7 KiB

120
README.md
View File

@ -10,110 +10,121 @@ Inspired by the [Windows 95 era PowerToys project](https://en.wikipedia.org/wiki
_(Note: in order to run PowerToys, you'll need to be running at least Windows build 17134 or higher. Also, PowerToys does not currently support Windows ARM machines)_
The latest release of PowerToys can be downloaded currently a few different ways. Our current recommended way is via GitHub.
### GitHub
The first preview of these utilities can be installed from the [PowerToys GitHub releases page](https://github.com/Microsoft/powertoys/releases).
The preview of these utilities can be installed from the [PowerToys GitHub releases page](https://github.com/Microsoft/powertoys/releases). Click on `Assets` to show the files available in the release and then click on `PowerToysSetup.msi` to download the PowerToys installer. <br />
PDB symbols for the release are available in a separate zip file `PDB symbols.zip`.
### Chocolatey (Unofficial)
Download and upgrade PowerToys from [Chocolatey](https://chocolatey.org).
To install PowerToys, run the following command from the command line or from PowerShell:
```powershell
choco install powertoys
```
To upgrade PowerToys, run the following command from the command line or from PowerShell:
```powershell
choco upgrade powertoys
```
If you have any issues when installing/upgrading the package please go to the [package page](https://chocolatey.org/packages/powertoys) and follow the [Chocolatey triage process](https://chocolatey.org/docs/package-triage-process)
### Build Status
## Build Status
[![Build Status](https://dev.azure.com/ms/PowerToys/_apis/build/status/microsoft.PowerToys?branchName=master)](https://dev.azure.com/ms/PowerToys/_build?definitionId=35096)
# What's Happening
## PowerToy Utilities
## October Update
A big thanks to everyone who has downloaded and started using the first PowerToys preview. There's been over 150K downloads so far. An even bigger thanks to everyone who has provided feedback and suggestions in the issues. The community engagement has been really awesome to see.
### FancyZones
The team has been hard at working fixing bugs, addressing issues and implementing feature suggestions. If you've downloaded the code and built the latest on your machine recently you've seen many of these improvements (dark mode!). We're hoping to have a new official build out shortly with all these improvements and will also be signed by Microsoft. A new utility is also coming soon: Chris Davis is working to integrate his [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
[FancyZones](/src/modules/fancyzones/) - FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts. The FancyZones backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md)
![SmartRename](https://github.com/chrdavis/SmartRename/raw/master/Images/SmartRenameDemo.gif)
### Shortcut
1. [FancyZones](/src/modules/fancyzones/) - FancyZones is a window manager that makes it easy to create complex window layouts and quickly position windows into those layouts. The FancyZones backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md)
[Windows key shortcut guide](/src/modules/shortcut_guide) - The shortcut guide appears when a user holds the Windows key down for more than one second and shows the available shortcuts for the current state of the desktop. The shortcut guide backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/ShortcutGuideBacklog.md)
![FancyZones](src/modules/fancyzones/FancyZones.png)
### PowerRename
FancyZones Video Tutorial
[![FancyZones Video Tutorial](doc/images/FZTutorial.jpg)](https://www.youtube.com/watch?v=rTtGzZYAXgY)
[PowerRename](/src/modules/powerrename) - PowerRename is a Windows Shell Extension for advanced bulk renaming using search and replace or regular expressions. PowerRename allows simple search and replace or more advanced regular expression matching. While you type in the search and replace input fields, the preview area will show what the items will be renamed to. PowerRename then calls into the Windows Explorer file operations engine to perform the rename. This has the benefit of allowing the rename operation to be undone after PowerRename exits.
2. [Windows key shortcut guide](/src/modules/shortcut_guide) - The shortcut guide appears when a user holds the Windows key down for more than one second and shows the available shortcuts for the current state of the desktop. The shortcut guide backlog can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/ShortcutGuideBacklog.md)
Chris Davis contributed his [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
![Windows key shortcut guide](doc/images/WindowsKeyShortcutGuide.jpg)
Additional utilities in the pipeline are:
### Additional utilities in the pipeline are
* Maximize to new desktop widget - The MTND widget shows a pop-up button when a user hovers over the maximize / restore button on any window. Clicking it creates a new desktop, sends the app to that desktop and maximizes the app on the new desktop.
* [Process terminate tool](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/Terminate%20Spec.md)
* [Animated gif screen recorder](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/GIF%20Maker%20Spec.md)
# Backlog
### Backlog
The full backlog of utilities can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/PowerToysBacklog.md)
# Where to download PowerToys
## What's Happening
The latest release of PowerToys can be downloaded from https://github.com/microsoft/PowerToys/releases <br />
Click on `Assets` to show the files available in the release and then click on `PowerToysSetup.msi` to download the PowerToys installer. <br />
PDB symbols for the release are available in a separate zip file `PDB symbols.zip`.
### October Update
# Developer Guidance
**Update** - We've shipped 0.12! This release includes bug fixes, addresses issues and implements many feature suggestions. This build and installer is also signed by Microsoft. Last, but not least, this release includes a new utility: Chris Davis has integrated [SmartRename tool](https://github.com/chrdavis/SmartRename) into PowerToys!
## Build Prerequisites
* Windows 10 1803 (build 10.0.17134.0) or above to build and run PowerToys.
* Visual Studio 2019 Community edition or higher, with the 'Desktop Development with C++' component and the Windows 10 SDK version 10.0.18362.0 or higher.
## Building the Code
* Open `powertoys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
* The PowerToys binaries will be in your repo under `x64\Release`.
* If you want to copy the `PowerToys.exe` binary to a different location, you'll also need to copy the `modules` and the `svgs` folders.
![SmartRename](https://github.com/microsoft/PowerToys/raw/master/src/modules/powerrename/images/PowerRenameDemo.gif)
## Prerequisites to Build the Installer
* Install the [WiX Toolset Visual Studio 2019 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WiXToolset).
* Install the [WiX Toolset build tools](https://wixtoolset.org/releases/).
## Building the .msi Installer
* From the `installer` folder open `PowerToysSetup.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
* The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
## Developer Guidance
## Debugging
The following configuration issue only applies if the user is a member of the Administrators group.
Some PowerToys modules require being run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations:
- the `FancyZones` module will be not be able to move an elevated window to a zone.
- the `Shortcut Guide` module will not appear if the foreground window belongs to an elevated application.
To run and debug PowerToys from Visual Studio when the user is a member of the Administrators group, Visual Studio has to be started with elevated privileges. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (/level='highestAvailable')` to `asInvoker (/level='asInvoker')`, save the changes.
## How to create new PowerToys
### Build Prerequisites
* Windows 10 1803 (build 10.0.17134.0) or above to build and run PowerToys.
* Visual Studio 2019 Community edition or higher, with the 'Desktop Development with C++' component and the Windows 10 SDK version 10.0.18362.0 or higher.
### Building the Code
* Open `powertoys.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
* The PowerToys binaries will be in your repo under `x64\Release`.
* If you want to copy the `PowerToys.exe` binary to a different location, you'll also need to copy the `modules` and the `svgs` folders.
### Prerequisites to Build the Installer
* Install the [WiX Toolset Visual Studio 2019 Extension](https://marketplace.visualstudio.com/items?itemName=RobMensching.WiXToolset).
* Install the [WiX Toolset build tools](https://wixtoolset.org/releases/).
### Building the .msi Installer
* From the `installer` folder open `PowerToysSetup.sln` in Visual Studio, in the `Solutions Configuration` drop-down menu select `Release` or `Debug`, from the `Build` menu choose `Build Solution`.
* The resulting `PowerToysSetup.msi` installer will be available in the `installer\PowerToysSetup\x64\Release\` folder.
### Debugging
The following configuration issue only applies if the user is a member of the Administrators group.
Some PowerToys modules require being run with the highest permission level if the current user is a member of the Administrators group. The highest permission level is required to be able to perform some actions when an elevated application (e.g. Task Manager) is in the foreground or is the target of an action. Without elevated privileges some PowerToys modules will still work but with some limitations:
* the `FancyZones` module will be not be able to move an elevated window to a zone.
* the `Shortcut Guide` module will not appear if the foreground window belongs to an elevated application.
To run and debug PowerToys from Visual Studio when the user is a member of the Administrators group, Visual Studio has to be started with elevated privileges. If you want to avoid running Visual Studio with elevated privileges and don't mind the limitations described above, you can do the following: open the `runner` project properties and navigate to the `Linker -> Manifest File` settings, edit the `UAC Execution Level` property and change it from `highestAvailable (level='highestAvailable')` to `asInvoker (/level='asInvoker')`, save the changes.
### How to create new PowerToys
See the instructions on [how to install the PowerToys Module project template](tools/project_template). <br />
Specifications for the [PowerToys settings API](doc/specs/PowerToys-settings.md).
## Coding Guidance
### Coding Guidance
Please review these brief docs below relating to our coding standards etc.
> 👉 If you find something missing from these docs, feel free to contribute to any of our documentation files anywhere in the repository (or make some new ones\!)
This is a work in progress as we learn what we'll need to provide people in order to be effective contributors to our project.
- [Coding Style](doc/coding/style.md)
- [Code Organization](doc/coding/organization.md)
# Contributing
* [Coding Style](doc/coding/style.md)
* [Code Organization](doc/coding/organization.md)
## Contributing
This project welcomes contributions and suggestions and we are excited to work with the power user community to build a set of tools for helping you get the most out of Windows.
We ask that **before you start work on a feature that you would like to contribute**, please read our [Contributor's Guide](contributing.md). We will be happy to work with you to figure out the best approach, provide guidance and mentorship throughout feature development, and help avoid any wasted or duplicate effort.
@ -126,11 +137,14 @@ When you submit a pull request, a CLA-bot will automatically determine whether y
a CLA and decorate the PR appropriately (e.g. status check, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
# Code of Conduct
## Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. <br />
For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments.
[conduct-code]: https://opensource.microsoft.com/codeofconduct/
[conduct-FAQ]: https://opensource.microsoft.com/codeofconduct/faq/
[conduct-email]: mailto:opencode@microsoft.com
## Privacy Statement
The application logs basic telemetry. Our Telemetry Data page (Coming Soon) has the trends from the telemetry. Please read the [Microsoft privacy statement](http://go.microsoft.com/fwlink/?LinkId=521839) for more information.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

View File

@ -6,7 +6,6 @@ The list below is the set of utilities we're considering and the rough priority
* Maximize to new desktop widget - The MTND widget shows a pop-up button when a user hovers over the maximize / restore button on any window. Clicking it creates a new desktop, sends the app to that desktop and maximizes the app on the new desktop.
* [Process terminate tool](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/Terminate%20Spec.md)
* [Batch file renamer](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/File%20Classification%20Spec.md)
* [Animated gif screen recorder](https://github.com/indierawk2k2/PowerToys-1/blob/master/specs/GIF%20Maker%20Spec.md)
## Backlog

View File

@ -7,7 +7,7 @@
<Product Id="*"
Name="PowerToys"
Language="1033"
Version="0.12.0"
Version="0.13.0"
Manufacturer="Microsoft"
UpgradeCode="42B84BF7-5FBF-473B-9C8B-049DC16F7708">
@ -17,7 +17,7 @@
<Upgrade Id="42B84BF7-5FBF-473B-9C8B-049DC16F7708">
<UpgradeVersion
Minimum="0.11.0" Maximum="0.12.0"
Minimum="0.11.0" Maximum="0.13.0"
Property="PREVIOUSVERSIONSINSTALLED"
IncludeMinimum="yes" IncludeMaximum="yes" />
</Upgrade>

View File

@ -367,7 +367,7 @@ UINT __stdcall TelemetryLogInstallSuccessCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Install::Success",
"Install_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -386,7 +386,7 @@ UINT __stdcall TelemetryLogInstallCancelCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Install::Cancel",
"Install_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -405,7 +405,7 @@ UINT __stdcall TelemetryLogInstallFailCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Install::Fail",
"Install_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -424,7 +424,7 @@ UINT __stdcall TelemetryLogUninstallSuccessCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Uninstall::Success",
"UnInstall_Success",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -443,7 +443,7 @@ UINT __stdcall TelemetryLogUninstallCancelCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Uninstall::Cancel",
"UnInstall_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -462,7 +462,7 @@ UINT __stdcall TelemetryLogUninstallFailCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Uninstall::Fail",
"UnInstall_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -481,7 +481,7 @@ UINT __stdcall TelemetryLogRepairCancelCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Repair::Cancel",
"Repair_Cancel",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
@ -500,7 +500,7 @@ UINT __stdcall TelemetryLogRepairFailCA(MSIHANDLE hInstall) {
TraceLoggingWrite(
g_hProvider,
"Repair::Fail",
"Repair_Fail",
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
TraceLoggingBoolean(TRUE, "UTCReplace_AppSessionGuid"),
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));

View File

@ -98,6 +98,7 @@
<ClInclude Include="d2d_window.h" />
<ClInclude Include="dpi_aware.h" />
<ClInclude Include="monitors.h" />
<ClInclude Include="on_thread_executor.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="settings_helpers.h" />
<ClInclude Include="settings_objects.h" />
@ -117,6 +118,7 @@
<ClCompile Include="d2d_window.cpp" />
<ClCompile Include="dpi_aware.cpp" />
<ClCompile Include="monitors.cpp" />
<ClCompile Include="on_thread_executor.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>

View File

@ -69,6 +69,9 @@
<ClInclude Include="version.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="on_thread_executor.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="d2d_svg.cpp">
@ -108,5 +111,8 @@
<ClCompile Include="dpi_aware.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="on_thread_executor.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

View File

@ -37,4 +37,8 @@ void DPIAware::Convert(HMONITOR monitor_handle, int &width, int &height) {
width = width * dpi_x / DEFAULT_DPI;
height = height * dpi_y / DEFAULT_DPI;
}
}
}
void DPIAware::EnableDPIAwarenessForThisProcess() {
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
}

View File

@ -1,12 +1,11 @@
#pragma once
#include "windef.h"
class DPIAware {
private:
static const int DEFAULT_DPI = 96;
struct DPIAware {
static constexpr int DEFAULT_DPI = 96;
public:
static HRESULT GetScreenDPIForWindow(HWND hwnd, UINT & dpi_x, UINT & dpi_y);
static HRESULT GetScreenDPIForPoint(POINT p, UINT& dpi_x, UINT& dpi_y);
static void Convert(HMONITOR monitor_handle, int &width, int &height);
static void EnableDPIAwarenessForThisProcess();
};

View File

@ -0,0 +1,38 @@
#include "pch.h"
#include "on_thread_executor.h"
OnThreadExecutor::OnThreadExecutor()
:_worker_thread{[this]() { worker_thread(); }}
{}
std::future<void> OnThreadExecutor::submit(task_t task) {
auto future = task.get_future();
std::lock_guard lock{_task_mutex};
_task_queue.emplace(std::move(task));
_task_cv.notify_one();
return future;
}
void OnThreadExecutor::worker_thread() {
while(_active) {
task_t task;
{
std::unique_lock task_lock{_task_mutex};
_task_cv.wait(task_lock, [this] { return !_task_queue.empty() || !_active; });
if(!_active) {
break;
}
task = std::move(_task_queue.front());
_task_queue.pop();
}
task();
}
}
OnThreadExecutor::~OnThreadExecutor() {
_active = false;
_task_cv.notify_one();
_worker_thread.join();
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <future>
#include <thread>
#include <functional>
#include <queue>
#include <atomic>
// OnThreadExecutor allows its caller to off-load some work to a persistently running background thread.
// This might come in handy if you use the API which sets thread-wide global state and the state needs
// to be isolated.
class OnThreadExecutor final {
public:
using task_t = std::packaged_task<void()>;
OnThreadExecutor();
~OnThreadExecutor();
std::future<void> submit(task_t task);
private:
void worker_thread();
std::thread _worker_thread;
std::mutex _task_mutex;
std::condition_variable _task_cv;
std::atomic_bool _active;
std::queue<std::packaged_task<void()>> _task_queue;
};

View File

@ -4,7 +4,7 @@
#define STRINGIZE(s) STRINGIZE2(s)
#define VERSION_MAJOR 0
#define VERSION_MINOR 12
#define VERSION_MINOR 13
#define VERSION_REVISION 0
#define VERSION_BUILD 0

View File

@ -8,7 +8,7 @@ An example PowerToy, that demonstrates how to create new ones.
#### [`fancyzones`](./fancyzones)
The FancyZones PowerToy that allows users to create custom zones on the screen, to which the windows will snap when moved.
#### [`FancyZonesEditor`](./FancyZonesEditor)
#### [`FancyZonesEditor`](./fancyzones/editor/FancyZonesEditor)
Editor for the snap-zones of the FancyZones PowerToy.
#### [`interface`](./interface)
@ -17,5 +17,5 @@ Definition of the interface used by the [`runner`](/src/runner) to manage the Po
#### [`powerrename`](./powerrename)
PowerRename is a Windows Shell Context Menu Extension for advanced bulk renaming using simple search and replace or more powerful regular expression matching.
#### [`shortcut_guide`](./shorcut_guide)
#### [`shortcut_guide`](./shortcut_guide)
The Windows Shortcut Guide, displayed when the WinKey is held for some time.

View File

@ -220,6 +220,7 @@ private:
void MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept;
void MoveSizeUpdate(POINT const& ptScreen) noexcept;
HANDLE m_movedWindow = nullptr;
winrt::com_ptr<IFancyZones> m_app;
winrt::com_ptr<IFancyZonesSettings> m_settings;
};
@ -298,6 +299,7 @@ void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcep
{
if (auto monitor = MonitorFromPoint(ptScreen, MONITOR_DEFAULTTONULL))
{
m_movedWindow = window;
m_app.as<IFancyZonesCallback>()->MoveSizeStart(window, monitor, ptScreen);
}
}
@ -305,8 +307,9 @@ void FancyZonesModule::MoveSizeStart(HWND window, POINT const& ptScreen) noexcep
void FancyZonesModule::MoveSizeEnd(HWND window, POINT const& ptScreen) noexcept
{
if (IsInterestingWindow(window))
if (IsInterestingWindow(window) || (window != nullptr && window == m_movedWindow))
{
m_movedWindow = nullptr;
m_app.as<IFancyZonesCallback>()->MoveSizeEnd(window, ptScreen);
}
}

View File

@ -23,6 +23,9 @@ namespace FancyZonesEditor
/// </summary>
public partial class MainWindow : MetroWindow
{
// TODO: share the constants b/w C# Editor and FancyZoneLib
public static int MAX_ZONES = 40;
public MainWindow()
{
InitializeComponent();
@ -57,7 +60,7 @@ namespace FancyZonesEditor
private void IncrementZones_Click(object sender, RoutedEventArgs e)
{
if (_settings.ZoneCount < 40)
if (_settings.ZoneCount < MAX_ZONES)
{
_settings.ZoneCount++;
}

View File

@ -279,7 +279,7 @@ namespace FancyZonesEditor
// 1 = unique key for per-monitor settings
// 2 = layoutid used to generate current layout (used to pick the default layout to show)
// 3 = handle to monitor (passed back to engine to persist data)
// 4 = X_Y_Width_Height (where EditorOverlay shows up)
// 4 = X_Y_Width_Height in a dpi-scaled-but-unaware coords (where EditorOverlay shows up)
// 5 = resolution key (passed back to engine to persist data)
// 6 = monitor DPI (float)

View File

@ -51,5 +51,5 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.12.0.0")]
[assembly: AssemblyFileVersion("0.12.0.0")]
[assembly: AssemblyVersion("0.13.0.0")]
[assembly: AssemblyFileVersion("0.13.0.0")]

View File

@ -1,13 +1,14 @@
#include "pch.h"
#include "common/dpi_aware.h"
#include "common/on_thread_executor.h"
struct FancyZones : public winrt::implements<FancyZones, IFancyZones, IFancyZonesCallback, IZoneWindowHost>
{
public:
FancyZones(HINSTANCE hinstance, IFancyZonesSettings* settings) noexcept
: m_hinstance(hinstance)
, m_settings(settings)
{
m_settings.attach(settings);
m_settings->SetCallback(this);
}
@ -84,10 +85,12 @@ private:
bool m_dragEnabled{}; // True if we should be showing zone hints while dragging
std::map<HMONITOR, winrt::com_ptr<IZoneWindow>> m_zoneWindowMap; // Map of monitor to ZoneWindow (one per monitor)
winrt::com_ptr<IZoneWindow> m_zoneWindowMoveSize; // "Active" ZoneWindow, where the move/size is happening. Will update as drag moves between monitors.
winrt::com_ptr<IFancyZonesSettings> m_settings;
IFancyZonesSettings* m_settings{};
GUID m_currentVirtualDesktopId{};
wil::unique_handle m_terminateEditorEvent;
OnThreadExecutor m_dpiUnawareThread;
static UINT WM_PRIV_VDCHANGED;
static UINT WM_PRIV_EDITOR;
@ -120,13 +123,18 @@ IFACEMETHODIMP_(void) FancyZones::Run() noexcept
RegisterHotKey(m_window, 1, m_settings->GetSettings().editorHotkey.get_modifiers(), m_settings->GetSettings().editorHotkey.get_code());
VirtualDesktopChanged();
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[]{
SetThreadDpiAwarenessContext(DPI_AWARENESS_CONTEXT_UNAWARE);
SetThreadDpiHostingBehavior(DPI_HOSTING_BEHAVIOR_MIXED);
}}).wait();
}
// IFancyZones
IFACEMETHODIMP_(void) FancyZones::Destroy() noexcept
{
std::unique_lock writeLock(m_lock);
m_zoneWindowMap.clear();
BufferedPaintUnInit();
if (m_window)
{
@ -239,22 +247,22 @@ void FancyZones::ToggleEditor() noexcept
}
HMONITOR monitor{};
UINT dpi_x = 96;
UINT dpi_y = 96;
HWND foregroundWindow{};
if (m_settings->GetSettings().use_cursorpos_editor_startupscreen)
UINT dpi_x = DPIAware::DEFAULT_DPI;
UINT dpi_y = DPIAware::DEFAULT_DPI;
const bool use_cursorpos_editor_startupscreen = m_settings->GetSettings().use_cursorpos_editor_startupscreen;
POINT currentCursorPos{};
if (use_cursorpos_editor_startupscreen)
{
POINT currentCursorPos{};
GetCursorPos(&currentCursorPos);
monitor = MonitorFromPoint(currentCursorPos, MONITOR_DEFAULTTOPRIMARY);
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
}
else
{
const HWND foregroundWindow = GetForegroundWindow();
foregroundWindow = GetForegroundWindow();
monitor = MonitorFromWindow(foregroundWindow, MONITOR_DEFAULTTOPRIMARY);
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
}
@ -272,21 +280,33 @@ void FancyZones::ToggleEditor() noexcept
MONITORINFOEX mi;
mi.cbSize = sizeof(mi);
GetMonitorInfo(monitor, &mi);
// X/Y need to start in unscaled screen coordinates to get to the proper top/left of the monitor
// From there, we need to scale the difference between the monitor and workarea rects to get the
// appropriate offset where the overlay should appear.
// This covers the cases where the taskbar is not at the bottom of the screen.
const auto x = mi.rcMonitor.left + MulDiv(mi.rcWork.left - mi.rcMonitor.left, 96, dpi_x);
const auto y = mi.rcMonitor.top + MulDiv(mi.rcWork.top - mi.rcMonitor.top, 96, dpi_y);
m_dpiUnawareThread.submit(OnThreadExecutor::task_t{[&]{
GetMonitorInfo(monitor, &mi);
}}).wait();
// Location that the editor should occupy, scaled by DPI
std::wstring editorLocation =
if(use_cursorpos_editor_startupscreen)
{
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
}
else
{
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
}
const auto taskbar_x_offset = MulDiv(mi.rcWork.left - mi.rcMonitor.left, DPIAware::DEFAULT_DPI, dpi_x);
const auto taskbar_y_offset = MulDiv(mi.rcWork.top - mi.rcMonitor.top, DPIAware::DEFAULT_DPI, dpi_y);
// Do not scale window params by the dpi, that will be done in the editor - see LayoutModel.Apply
const auto x = mi.rcMonitor.left + taskbar_x_offset;
const auto y = mi.rcMonitor.top + taskbar_y_offset;
const auto width = mi.rcWork.right - mi.rcWork.left;
const auto height = mi.rcWork.bottom - mi.rcWork.top;
const std::wstring editorLocation =
std::to_wstring(x) + L"_" +
std::to_wstring(y) + L"_" +
std::to_wstring(MulDiv(mi.rcWork.right - mi.rcWork.left, 96, dpi_x)) + L"_" +
std::to_wstring(MulDiv(mi.rcWork.bottom - mi.rcWork.top, 96, dpi_y));
std::to_wstring(width) + L"_" +
std::to_wstring(height);
const std::wstring params =
iter->second->UniqueId() + L" " +
@ -294,7 +314,7 @@ void FancyZones::ToggleEditor() noexcept
std::to_wstring(reinterpret_cast<UINT_PTR>(monitor)) + L" " +
editorLocation + L" " +
iter->second->WorkAreaKey() + L" " +
std::to_wstring(static_cast<float>(dpi_x) / 96.0f);
std::to_wstring(static_cast<float>(dpi_x) / DPIAware::DEFAULT_DPI);
SHELLEXECUTEINFO sei{ sizeof(sei) };
sei.fMask = { SEE_MASK_NOCLOSEPROCESS | SEE_MASK_FLAG_NO_UI };
@ -652,34 +672,40 @@ void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT cons
RECT windowRect{};
::GetWindowRect(window, &windowRect);
windowRect.top += 6;
windowRect.left += 8;
windowRect.right -= 8;
windowRect.bottom -= 6;
const auto padding_x = 8;
const auto padding_y = 6;
windowRect.top += padding_y;
windowRect.left += padding_x;
windowRect.right -= padding_x;
windowRect.bottom -= padding_y;
if (PtInRect(&windowRect, ptScreen))
if (PtInRect(&windowRect, ptScreen) == FALSE)
{
m_inMoveSize = true;
return;
}
auto iter = m_zoneWindowMap.find(monitor);
if (iter != m_zoneWindowMap.end())
{
m_windowMoveSize = window;
m_inMoveSize = true;
// This updates m_dragEnabled depending on if the shift key is being held down.
UpdateDragState(writeLock);
auto iter = m_zoneWindowMap.find(monitor);
if (iter == end(m_zoneWindowMap))
{
return;
}
if (m_dragEnabled)
{
m_zoneWindowMoveSize = iter->second;
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
}
else if (m_zoneWindowMoveSize)
{
m_zoneWindowMoveSize->MoveSizeCancel();
m_zoneWindowMoveSize = nullptr;
}
}
m_windowMoveSize = window;
// This updates m_dragEnabled depending on if the shift key is being held down.
UpdateDragState(writeLock);
if (m_dragEnabled)
{
m_zoneWindowMoveSize = iter->second;
m_zoneWindowMoveSize->MoveSizeEnter(window, m_dragEnabled);
}
else if (m_zoneWindowMoveSize)
{
m_zoneWindowMoveSize->MoveSizeCancel();
m_zoneWindowMoveSize = nullptr;
}
}

View File

@ -11,7 +11,7 @@ public:
LoadSettings(name, true /*fromFile*/);
}
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback.attach(callback); }
IFACEMETHODIMP_(void) SetCallback(IFancyZonesCallback* callback) { m_callback = callback; }
IFACEMETHODIMP_(bool) GetConfig(_Out_ PWSTR buffer, _Out_ int *buffer_sizeg) noexcept;
IFACEMETHODIMP_(void) SetConfig(PCWSTR config) noexcept;
IFACEMETHODIMP_(void) CallCustomAction(PCWSTR action) noexcept;
@ -21,7 +21,7 @@ private:
void LoadSettings(PCWSTR config, bool fromFile) noexcept;
void SaveSettings() noexcept;
winrt::com_ptr<IFancyZonesCallback> m_callback;
IFancyZonesCallback* m_callback{};
const HINSTANCE m_hinstance;
PCWSTR m_name{};

View File

@ -34,13 +34,15 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
#define VERSION_PERSISTEDDATA 0x0000F00D
struct ZoneSetPersistedData
{
static constexpr inline size_t MAX_ZONES = 40;
DWORD Version{VERSION_PERSISTEDDATA};
WORD LayoutId{};
DWORD ZoneCount{};
ZoneSetLayout Layout{};
DWORD PaddingInner{};
DWORD PaddingOuter{};
RECT Zones[40]{};
RECT Zones[MAX_ZONES]{};
};
struct ZoneSetConfig

View File

@ -1,4 +1,6 @@
#include "pch.h"
#include <common/dpi_aware.h>
#include <ShellScalingApi.h>
struct ZoneWindow : public winrt::implements<ZoneWindow, IZoneWindow>
@ -116,78 +118,79 @@ ZoneWindow::ZoneWindow(
MONITORINFO mi{};
mi.cbSize = sizeof(mi);
if (GetMonitorInfoW(m_monitor, &mi))
if (!GetMonitorInfoW(m_monitor, &mi))
{
const UINT dpi = GetDpiForMonitor();
const Rect monitorRect(mi.rcMonitor);
const Rect workAreaRect(mi.rcWork, dpi);
return;
}
const UINT dpi = GetDpiForMonitor();
const Rect monitorRect(mi.rcMonitor);
const Rect workAreaRect(mi.rcWork, dpi);
StringCchPrintf(m_workArea, ARRAYSIZE(m_workArea), L"%d_%d", monitorRect.width(), monitorRect.height());
StringCchPrintf(m_workArea, ARRAYSIZE(m_workArea), L"%d_%d", monitorRect.width(), monitorRect.height());
InitializeId(deviceId, virtualDesktopId);
LoadSettings();
InitializeZoneSets();
InitializeId(deviceId, virtualDesktopId);
LoadSettings();
InitializeZoneSets();
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = s_WndProc;
wcex.hInstance = hinstance;
wcex.lpszClassName = L"SuperFancyZones_ZoneWindow";
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
RegisterClassExW(&wcex);
WNDCLASSEXW wcex{};
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.lpfnWndProc = s_WndProc;
wcex.hInstance = hinstance;
wcex.lpszClassName = L"SuperFancyZones_ZoneWindow";
wcex.hCursor = LoadCursorW(nullptr, IDC_ARROW);
RegisterClassExW(&wcex);
m_window = wil::unique_hwnd {
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP,
m_window = wil::unique_hwnd {
CreateWindowExW(WS_EX_TOOLWINDOW, L"SuperFancyZones_ZoneWindow", L"", WS_POPUP,
workAreaRect.left(), workAreaRect.top(), workAreaRect.width(), workAreaRect.height(),
nullptr, nullptr, hinstance, this)
};
};
if (m_window)
if (m_window)
{
MakeWindowTransparent(m_window.get());
UpdateGrid(0, 0);
if (flashZones)
{
MakeWindowTransparent(m_window.get());
UpdateGrid(0, 0);
if (flashZones)
{
FlashZones();
}
FlashZones();
}
}
}
IFACEMETHODIMP ZoneWindow::ShowZoneWindow(bool activate, bool fadeIn) noexcept
{
if (m_window)
if (!m_window)
{
m_flashMode = false;
UINT flags = SWP_NOSIZE | SWP_NOMOVE;
if (!activate)
{
WI_SetFlag(flags, SWP_NOACTIVATE);
}
if (!fadeIn)
{
WI_SetFlag(flags, SWP_SHOWWINDOW);
}
HWND windowInsertAfter = m_windowMoveSize;
if (windowInsertAfter == nullptr)
{
windowInsertAfter = HWND_TOPMOST;
}
SetWindowPos(m_window.get(), windowInsertAfter, 0, 0, 0, 0, flags);
if (fadeIn)
{
AnimateWindow(m_window.get(), m_showAnimationDuration, AW_BLEND);
InvalidateRect(m_window.get(), nullptr, true);
}
return S_OK;
return E_FAIL;
}
return E_FAIL;
m_flashMode = false;
UINT flags = SWP_NOSIZE | SWP_NOMOVE;
if (!activate)
{
WI_SetFlag(flags, SWP_NOACTIVATE);
}
if (!fadeIn)
{
WI_SetFlag(flags, SWP_SHOWWINDOW);
}
HWND windowInsertAfter = m_windowMoveSize;
if (windowInsertAfter == nullptr)
{
windowInsertAfter = HWND_TOPMOST;
}
SetWindowPos(m_window.get(), windowInsertAfter, 0, 0, 0, 0, flags);
if (fadeIn)
{
AnimateWindow(m_window.get(), m_showAnimationDuration, AW_BLEND);
InvalidateRect(m_window.get(), nullptr, true);
}
return S_OK;
}
IFACEMETHODIMP ZoneWindow::HideZoneWindow() noexcept
@ -356,54 +359,56 @@ void ZoneWindow::InitializeZoneSets() noexcept
void ZoneWindow::LoadZoneSetsFromRegistry() noexcept
{
if (wil::unique_hkey key{ RegistryHelpers::OpenKey(m_workArea) })
wil::unique_hkey key{RegistryHelpers::OpenKey(m_workArea)};
if (!key)
{
ZoneSetPersistedData data{};
DWORD dataSize = sizeof(data);
wchar_t value[256]{};
DWORD valueLength = ARRAYSIZE(value);
DWORD i = 0;
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
return;
}
ZoneSetPersistedData data{};
DWORD dataSize = sizeof(data);
wchar_t value[256]{};
DWORD valueLength = ARRAYSIZE(value);
DWORD i = 0;
while (RegEnumValueW(key.get(), i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
{
if (data.Version == VERSION_PERSISTEDDATA)
{
if (data.Version == VERSION_PERSISTEDDATA)
GUID zoneSetId;
if (SUCCEEDED_LOG(CLSIDFromString(value, &zoneSetId)))
{
GUID zoneSetId;
if (SUCCEEDED_LOG(CLSIDFromString(value, &zoneSetId)))
auto zoneSet = MakeZoneSet(ZoneSetConfig(
zoneSetId,
data.LayoutId,
m_monitor,
m_workArea,
data.Layout,
0,
static_cast<int>(data.PaddingInner),
static_cast<int>(data.PaddingOuter)));
if (zoneSet)
{
auto zoneSet = MakeZoneSet(ZoneSetConfig(
zoneSetId,
data.LayoutId,
m_monitor,
m_workArea,
data.Layout,
0,
static_cast<int>(data.PaddingInner),
static_cast<int>(data.PaddingOuter)));
if (zoneSet)
for (UINT j = 0; j < data.ZoneCount; j++)
{
for (UINT j = 0; j < data.ZoneCount; j++)
{
zoneSet->AddZone(MakeZone(data.Zones[j]), false);
}
m_zoneSets.emplace_back(zoneSet);
if (zoneSetId == m_activeZoneSetId)
{
UpdateActiveZoneSet(zoneSet.get());
}
zoneSet->AddZone(MakeZone(data.Zones[j]), false);
}
if (zoneSetId == m_activeZoneSetId)
{
UpdateActiveZoneSet(zoneSet.get());
}
m_zoneSets.emplace_back(std::move(zoneSet));
}
}
else
{
// Migrate from older settings format
}
valueLength = ARRAYSIZE(value);
dataSize = sizeof(data);
}
else
{
// Migrate from older settings format
}
valueLength = ARRAYSIZE(value);
dataSize = sizeof(data);
}
}
@ -716,37 +721,38 @@ void ZoneWindow::DrawZone(wil::unique_hdc& hdc, ColorSetting const& colorSetting
}
FillRectARGB(hdc, &zoneRect, colorSetting.fillAlpha, colorSetting.fill, false);
if (!m_flashMode)
if (m_flashMode)
{
COLORREF const colorFill = RGB(255, 255, 255);
size_t const index = zone->Id();
int const padding = 5;
int const size = 10;
POINT offset = { zoneRect.left + padding, zoneRect.top + padding };
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, false, false, colorFill); // top left
return;
}
offset.x = zoneRect.right - ((padding + size) * 3);
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, true, false, colorFill); // top right
return;
}
offset.y = zoneRect.bottom - ((padding + size) * 3);
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, true, true, colorFill); // bottom right
return;
}
offset.x = zoneRect.left + padding;
DrawIndex(hdc, offset, index, padding, size, false, true, colorFill); // bottom left
return;
}
COLORREF const colorFill = RGB(255, 255, 255);
size_t const index = zone->Id();
int const padding = 5;
int const size = 10;
POINT offset = { zoneRect.left + padding, zoneRect.top + padding };
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, false, false, colorFill); // top left
return;
}
offset.x = zoneRect.right - ((padding + size) * 3);
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, true, false, colorFill); // top right
return;
}
offset.y = zoneRect.bottom - ((padding + size) * 3);
if (!IsOccluded(offset, index))
{
DrawIndex(hdc, offset, index, padding, size, true, true, colorFill); // bottom right
return;
}
offset.x = zoneRect.left + padding;
DrawIndex(hdc, offset, index, padding, size, false, true, colorFill); // bottom left
}
void ZoneWindow::DrawIndex(wil::unique_hdc& hdc, POINT offset, size_t index, int padding, int size, bool flipX, bool flipY, COLORREF colorFill)
@ -817,33 +823,36 @@ void ZoneWindow::DrawActiveZoneSet(wil::unique_hdc& hdc, RECT const& clientRect)
ColorSetting const colorFlash { 200, RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
auto zones = m_activeZoneSet->GetZones();
size_t colorIndex = zones.size() - 1;
const size_t maxColorIndex = min(size(zones) - 1, size(colors) - 1);
size_t colorIndex = maxColorIndex;
for (auto iter = zones.rbegin(); iter != zones.rend(); iter++)
{
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
if (!zone)
{
if (zone != m_highlightZone)
{
if (m_flashMode)
{
DrawZone(hdc, colorFlash, zone);
}
else if (m_drawHints)
{
DrawZone(hdc, colorHints, zone);
}
else if (m_editorMode)
{
DrawZone(hdc, colorEditorMode, zone);
}
else
{
colorViewer.fill = colors[colorIndex];
DrawZone(hdc, colorViewer, zone);
}
}
colorIndex--;
continue;
}
if (zone != m_highlightZone)
{
if (m_flashMode)
{
DrawZone(hdc, colorFlash, zone);
}
else if (m_drawHints)
{
DrawZone(hdc, colorHints, zone);
}
else if (m_editorMode)
{
DrawZone(hdc, colorEditorMode, zone);
}
else
{
colorViewer.fill = colors[colorIndex];
DrawZone(hdc, colorViewer, zone);
}
}
colorIndex = colorIndex != 0 ? colorIndex - 1 : maxColorIndex;
}
if (m_highlightZone)
@ -930,7 +939,7 @@ void ZoneWindow::OnPaint(wil::unique_hdc& hdc) noexcept
RECT clientRect;
GetClientRect(m_window.get(), &clientRect);
wil::unique_hdc hdcMem;;
wil::unique_hdc hdcMem;
HPAINTBUFFER bufferedPaint = BeginBufferedPaint(hdc.get(), &clientRect, BPBF_TOPDOWNDIB, nullptr, &hdcMem);
if (bufferedPaint)
{
@ -1284,7 +1293,7 @@ UINT ZoneWindow::GetDpiForMonitor() noexcept
}
}
return (dpi == 0) ? 96 : dpi;
return (dpi == 0) ? DPIAware::DEFAULT_DPI : dpi;
}
#pragma endregion

View File

@ -18,7 +18,7 @@ The text to replace the instance(s) in the item name matched by the Search text
## Options
### Use Regular Expressions
If checked, the Search field will be interpreted as a regular expression. If not checked, the Search field will be used as a text to be replaced with the text in the Replace field.
If checked, the Search field will be interpreted as a regular expression. The Replace field can also contain regex variables (see examples below). If not checked, the Search field will be used as a text to be replaced with the text in the Replace field.
### Case Sensitive
If checked, the text specified in the Search field will only match text in the items if the text is the same case. By default we match case insensitive.
@ -53,9 +53,13 @@ Ex: txt.txt -> txt.NewExtension
For most use cases, a simple search and replace is sufficient. Other users will need more control over. That is where Regular Expressions come in. Regular Expressions define a search pattern for text. Regular expressions can be used to search, edit and manipulate text. The pattern defined by the regular expression may match one or several times or not at all for a given string. PowerRename uses the ECMAScript grammar, which is common amongst modern programming languages.
To enable regular expressions, check the "Use Regular Expressions" checkbox. Note: You will likely want to check "Match All Occurrences" while using regular expressions.
### Examples
| RegEx | Description |
Simple matching examples:
| Search for | Description |
| -------------- | ------------- |
| .* | Match all the text in the name |
| ^foo | Match text that begins with "foo" |
@ -64,7 +68,16 @@ For most use cases, a simple search and replace is sufficient. Other users will
| .+?(?=bar) | Match everything up to "bar" |
| foo[\s\S]\*bar | Match everything between "foo" and "bar" |
Note: you will likely want to check Match All Occurrences while using regular expressions
Matching and variable examples:
| Search for | Replace With | Description |
| ---------- | ------------- |--------------------------------------------|
| (.\*).png | foo\_$1.png | Prepends "foo\_" to the existing file name |
| (.\*).png | $1\_foo.png | Appends "\_foo" to the existing file name |
| (.\*) | $1.txt | Appends ".txt" extension to existing file name |
| (^\w+\.$)\|(^\w+$) | $2.txt | Appends ".txt" extension to existing file name only if it does not have an extension |
### External Help
There are great examples/cheat sheets available online to help you

View File

@ -8,8 +8,11 @@
extern HINSTANCE g_hInst;
HWND g_hwndParent = 0;
struct InvokeStruct
{
HWND hwndParent;
IStream* pstrm;
};
const wchar_t powerRenameRegPath[] = L"Software\\Microsoft\\PowerRename";
const wchar_t powerRenameRegEnabledName[] = L"Enabled";
@ -73,15 +76,12 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
return E_FAIL;
HRESULT hr = E_UNEXPECTED;
if (m_spdo)
if (m_spdo && !(uFlags & (CMF_DEFAULTONLY | CMF_VERBSONLY | CMF_OPTIMIZEFORINVOKE)))
{
if ((uFlags & ~CMF_OPTIMIZEFORINVOKE) && (uFlags & ~(CMF_DEFAULTONLY | CMF_VERBSONLY)))
{
wchar_t menuName[64] = { 0 };
LoadString(g_hInst, IDS_POWERRENAME, menuName, ARRAYSIZE(menuName));
InsertMenu(hMenu, index, MF_STRING | MF_BYPOSITION, uIDFirst++, menuName);
hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
}
wchar_t menuName[64] = { 0 };
LoadString(g_hInst, IDS_POWERRENAME, menuName, ARRAYSIZE(menuName));
InsertMenu(hMenu, index, MF_STRING | MF_BYPOSITION, uIDFirst++, menuName);
hr = MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 1);
}
return hr;
@ -91,19 +91,29 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
{
HRESULT hr = E_FAIL;
if (IsEnabled() &
if (IsEnabled() &&
(IS_INTRESOURCE(pici->lpVerb)) &&
(LOWORD(pici->lpVerb) == 0))
{
Trace::Invoked();
IStream* pstrm = nullptr;
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &pstrm);
InvokeStruct* pInvokeData = new InvokeStruct;
hr = pInvokeData ? S_OK : E_OUTOFMEMORY;
if (SUCCEEDED(hr))
{
if (!SHCreateThread(s_PowerRenameUIThreadProc, pstrm, CTF_COINIT | CTF_PROCESS_REF, nullptr))
pInvokeData->hwndParent = pici->hwnd;
hr = CoMarshalInterThreadInterfaceInStream(__uuidof(m_spdo), m_spdo, &(pInvokeData->pstrm));
if (SUCCEEDED(hr))
{
pstrm->Release(); // if we failed to create the thread, then we must release the stream
hr = E_FAIL;
hr = SHCreateThread(s_PowerRenameUIThreadProc, pInvokeData, CTF_COINIT | CTF_PROCESS_REF, nullptr) ? S_OK : E_FAIL;
if (FAILED(hr))
{
pInvokeData->pstrm->Release(); // if we failed to create the thread, then we must release the stream
}
}
if (FAILED(hr))
{
delete pInvokeData;
}
}
Trace::InvokedRet(hr);
@ -114,9 +124,9 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
{
IStream* pstrm = static_cast<IStream*>(pData);
InvokeStruct* pInvokeData = static_cast<InvokeStruct*>(pData);
CComPtr<IDataObject> spdo;
HRESULT hr = CoGetInterfaceAndReleaseStream(pstrm, IID_PPV_ARGS(&spdo));
HRESULT hr = CoGetInterfaceAndReleaseStream(pInvokeData->pstrm, IID_PPV_ARGS(&spdo));
if (SUCCEEDED(hr))
{
// Create the smart rename manager
@ -139,7 +149,7 @@ DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
if (SUCCEEDED(hr))
{
// Call blocks until we are done
spsrui->Show();
spsrui->Show(pInvokeData->hwndParent);
spsrui->Close();
}
}
@ -150,6 +160,8 @@ DWORD WINAPI CPowerRenameMenu::s_PowerRenameUIThreadProc(_In_ void* pData)
}
}
delete pInvokeData;
Trace::UIShownRet(hr);
return 0;

View File

@ -1,6 +1,7 @@
// Microsoft Visual C++ generated resource script.
⼀⼀ഀ<EFBFBD>
⌀椀渀挀氀甀搀攀 ∀爀攀猀漀甀爀挀攀⸀栀∀ഀ<EFBFBD>
#include "../../../common/version.h"
<EFBFBD>
⌀搀攀昀椀渀攀 䄀倀匀吀唀䐀䤀伀开刀䔀䄀䐀伀一䰀夀开匀夀䴀䈀伀䰀匀ഀ<EFBFBD>
⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀⼀ഀ<EFBFBD>
@ -50,8 +51,8 @@
⼀⼀ഀ<EFBFBD>
<EFBFBD>
嘀匀开嘀䔀刀匀䤀伀一开䤀一䘀伀 嘀䔀刀匀䤀伀一䤀一䘀伀ഀ<EFBFBD>
FILEVERSION 0,12,0,0
PRODUCTVERSION 0,12,0,0
FILEVERSION FILE_VERSION
PRODUCTVERSION PRODUCT_VERSION
 䘀䤀䰀䔀䘀䰀䄀䜀匀䴀䄀匀䬀  砀㌀昀䰀ഀ<EFBFBD>
⌀椀昀搀攀昀 开䐀䔀䈀唀䜀ഀ<EFBFBD>
 䘀䤀䰀䔀䘀䰀䄀䜀匀  砀㄀䰀ഀ<EFBFBD>
@ -66,14 +67,14 @@
    䈀䔀䜀䤀一ഀ<EFBFBD>
        䈀䰀伀䌀䬀    㐀戀 ∀ഀ<EFBFBD>
        䈀䔀䜀䤀一ഀ<EFBFBD>
VALUE "CompanyName", "Microsoft Corp."
VALUE "CompanyName", COMPANY_NAME
            嘀䄀䰀唀䔀 ∀䘀椀氀攀䐀攀猀挀爀椀瀀琀椀漀渀∀Ⰰ ∀倀漀眀攀爀刀攀渀愀洀攀 倀漀眀攀爀吀漀礀∀ഀ<EFBFBD>
VALUE "FileVersion", "0.12.0.0"
VALUE "FileVersion", FILE_VERSION_STRING
            嘀䄀䰀唀䔀 ∀䤀渀琀攀爀渀愀氀一愀洀攀∀Ⰰ ∀倀漀眀攀爀刀攀渀愀洀攀䔀砀琀⸀搀氀氀∀ഀ<EFBFBD>
VALUE "LegalCopyright", "Copyright (C) 2019"
VALUE "LegalCopyright", COPYRIGHT_NOTE
            嘀䄀䰀唀䔀 ∀伀爀椀最椀渀愀氀䘀椀氀攀渀愀洀攀∀Ⰰ ∀倀漀眀攀爀刀攀渀愀洀攀䔀砀琀⸀搀氀氀∀ഀ<EFBFBD>
            嘀䄀䰀唀䔀 ∀倀爀漀搀甀挀琀一愀洀攀∀Ⰰ ∀倀漀眀攀爀刀攀渀愀洀攀 倀漀眀攀爀吀漀礀∀ഀ<EFBFBD>
VALUE "ProductVersion", "0.12.0.0"
VALUE "ProductVersion", PRODUCT_VERSION_STRING
        䔀一䐀ഀ<EFBFBD>
    䔀一䐀ഀ<EFBFBD>
    䈀䰀伀䌀䬀 ∀嘀愀爀䘀椀氀攀䤀渀昀漀∀ഀ<EFBFBD>

View File

@ -101,7 +101,7 @@ BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, void*)
break;
case DLL_PROCESS_DETACH:
Trace::RegisterProvider();
Trace::UnregisterProvider();
break;
}
return TRUE;

View File

@ -102,7 +102,7 @@ public:
interface __declspec(uuid("E6679DEB-460D-42C1-A7A8-E25897061C99")) IPowerRenameUI : public IUnknown
{
public:
IFACEMETHOD(Show)() = 0;
IFACEMETHOD(Show)(_In_opt_ HWND hwndParent) = 0;
IFACEMETHOD(Close)() = 0;
IFACEMETHOD(Update)() = 0;
};

View File

@ -203,7 +203,7 @@ HRESULT CPowerRenameItem::_Init(_In_ IShellItem* psi)
HRESULT hr = psi->GetDisplayName(SIGDN_FILESYSPATH, &m_path);
if (SUCCEEDED(hr))
{
hr = psi->GetDisplayName(SIGDN_NORMALDISPLAY, &m_originalName);
hr = SHStrDup(PathFindFileName(m_path), &m_originalName);
if (SUCCEEDED(hr))
{
// Check if we are a folder now so we can check this attribute quickly later

View File

@ -213,7 +213,7 @@ HRESULT CPowerRenameRegEx::Replace(_In_ PCWSTR source, _Outptr_ PWSTR* result)
std::wsmatch m;
if (std::regex_search(sourceToUse, m, pattern))
{
res = sourceToUse.replace(m.prefix().length(), searchTerm.length(), replaceTerm);
res = sourceToUse.replace(m.prefix().length(), m.length(), replaceTerm);
}
}
}

View File

@ -47,7 +47,7 @@ int APIENTRY wWinMain(
if (SUCCEEDED(CPowerRenameUI::s_CreateInstance(spsrm, nullptr, true, &spsrui)))
{
// Call blocks until we are done
spsrui->Show();
spsrui->Show(NULL);
spsrui->Close();
// Need to call shutdown to break circular dependencies

View File

@ -32,16 +32,48 @@ struct FlagCheckboxMap
FlagCheckboxMap g_flagCheckboxMap[] =
{
{ UseRegularExpressions, IDC_CHECK_USEREGEX },
{ ExcludeSubfolders, IDC_CHECK_EXCLUDESUBFOLDERS },
{ EnumerateItems, IDC_CHECK_ENUMITEMS },
{ ExcludeFiles, IDC_CHECK_EXCLUDEFILES },
{ CaseSensitive, IDC_CHECK_CASESENSITIVE },
{ MatchAllOccurences, IDC_CHECK_MATCHALLOCCRENCES },
{ ExcludeFolders, IDC_CHECK_EXCLUDEFOLDERS },
{ NameOnly, IDC_CHECK_NAMEONLY },
{ ExtensionOnly, IDC_CHECK_EXTENSIONONLY }
{ ExcludeSubfolders, IDC_CHECK_EXCLUDESUBFOLDERS },
{ EnumerateItems, IDC_CHECK_ENUMITEMS },
{ ExcludeFiles, IDC_CHECK_EXCLUDEFILES },
{ CaseSensitive, IDC_CHECK_CASESENSITIVE },
{ MatchAllOccurences, IDC_CHECK_MATCHALLOCCURENCES },
{ ExcludeFolders, IDC_CHECK_EXCLUDEFOLDERS },
{ NameOnly, IDC_CHECK_NAMEONLY },
{ ExtensionOnly, IDC_CHECK_EXTENSIONONLY }
};
struct RepositionMap
{
DWORD id;
DWORD flags;
};
enum
{
Reposition_None = 0,
Reposition_X = 0x1,
Reposition_Y = 0x2,
Reposition_Width = 0x4,
Reposition_Height = 0x8
};
RepositionMap g_repositionMap[] =
{
{ IDC_SEARCHREPLACEGROUP, Reposition_Width },
{ IDC_OPTIONSGROUP, Reposition_Width },
{ IDC_PREVIEWGROUP, Reposition_Width | Reposition_Height },
{ IDC_EDIT_SEARCHFOR, Reposition_Width },
{ IDC_EDIT_REPLACEWITH, Reposition_Width },
{ IDC_LIST_PREVIEW, Reposition_Width | Reposition_Height },
{ IDC_STATUS_MESSAGE, Reposition_Y },
{ ID_RENAME, Reposition_X | Reposition_Y },
{ ID_ABOUT, Reposition_X | Reposition_Y },
{ IDCANCEL, Reposition_X | Reposition_Y }
};
inline int RECT_WIDTH(RECT& r) { return r.right - r.left; }
inline int RECT_HEIGHT(RECT& r) { return r.bottom - r.top; }
// IUnknown
IFACEMETHODIMP CPowerRenameUI::QueryInterface(__in REFIID riid, __deref_out void** ppv)
{
@ -89,9 +121,9 @@ HRESULT CPowerRenameUI::s_CreateInstance(_In_ IPowerRenameManager* psrm, _In_opt
}
// IPowerRenameUI
IFACEMETHODIMP CPowerRenameUI::Show()
IFACEMETHODIMP CPowerRenameUI::Show(_In_opt_ HWND hwndParent)
{
return _DoModal(NULL);
return _DoModeless(hwndParent);
}
IFACEMETHODIMP CPowerRenameUI::Close()
@ -184,7 +216,7 @@ IFACEMETHODIMP CPowerRenameUI::OnRenameCompleted()
EnableWindow(m_hwnd, TRUE);
// Close the window
_OnCloseDlg();
PostMessage(m_hwnd, WM_CLOSE, (WPARAM)0, (LPARAM)0);
return S_OK;
}
@ -322,12 +354,25 @@ void CPowerRenameUI::_OnCloseDlg()
{
// Persist the current settings
_WriteSettings();
EndDialog(m_hwnd, 1);
if (m_modeless)
{
DestroyWindow(m_hwnd);
}
else
{
EndDialog(m_hwnd, 1);
}
}
void CPowerRenameUI::_OnDestroyDlg()
{
_Cleanup();
if (m_modeless)
{
PostQuitMessage(0);
}
}
void CPowerRenameUI::_OnRename()
@ -352,6 +397,7 @@ void CPowerRenameUI::_OnAbout()
HRESULT CPowerRenameUI::_DoModal(__in_opt HWND hwnd)
{
m_modeless = false;
HRESULT hr = S_OK;
INT_PTR ret = DialogBoxParam(g_hInst, MAKEINTRESOURCE(IDD_MAIN), hwnd, s_DlgProc, (LPARAM)this);
if (ret < 0)
@ -361,6 +407,33 @@ HRESULT CPowerRenameUI::_DoModal(__in_opt HWND hwnd)
return hr;
}
HRESULT CPowerRenameUI::_DoModeless(__in_opt HWND hwnd)
{
m_modeless = true;
HRESULT hr = S_OK;
if (NULL != CreateDialogParam(g_hInst, MAKEINTRESOURCE(IDD_MAIN), hwnd, s_DlgProc, (LPARAM)this))
{
ShowWindow(m_hwnd, SW_SHOWNORMAL);
MSG msg;
while (GetMessage(&msg, NULL, 0, 0))
{
if (!IsDialogMessage(m_hwnd, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
DestroyWindow(m_hwnd);
m_hwnd = NULL;
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
INT_PTR CPowerRenameUI::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
INT_PTR bRet = TRUE; // default for all handled cases in switch below
@ -379,6 +452,14 @@ INT_PTR CPowerRenameUI::_DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
bRet = _OnNotify(wParam, lParam);
break;
case WM_SIZE:
_OnSize(wParam);
break;
case WM_GETMINMAXINFO:
_OnGetMinMaxInfo(lParam);
break;
case WM_CLOSE:
_OnCloseDlg();
break;
@ -429,6 +510,13 @@ void CPowerRenameUI::_OnInitDlg()
RegisterDragDrop(m_hwnd, this);
}
RECT rc = { 0 };
GetWindowRect(m_hwnd, &rc);
m_initialWidth = RECT_WIDTH(rc);
m_initialHeight = RECT_HEIGHT(rc);
m_lastWidth = m_initialWidth;
m_lastHeight = m_initialHeight;
// Disable rename button by default. It will be enabled in _UpdateCounts if
// there are tiems to be renamed
EnableWindow(GetDlgItem(m_hwnd, ID_RENAME), FALSE);
@ -469,7 +557,7 @@ void CPowerRenameUI::_OnCommand(_In_ WPARAM wParam, _In_ LPARAM lParam)
case IDC_CHECK_EXCLUDEFILES:
case IDC_CHECK_EXCLUDEFOLDERS:
case IDC_CHECK_EXCLUDESUBFOLDERS:
case IDC_CHECK_MATCHALLOCCRENCES:
case IDC_CHECK_MATCHALLOCCURENCES:
case IDC_CHECK_USEREGEX:
case IDC_CHECK_EXTENSIONONLY:
case IDC_CHECK_NAMEONLY:
@ -543,6 +631,90 @@ BOOL CPowerRenameUI::_OnNotify(_In_ WPARAM wParam, _In_ LPARAM lParam)
return ret;
}
void CPowerRenameUI::_OnGetMinMaxInfo(_In_ LPARAM lParam)
{
if (m_initialWidth)
{
// Prevent resizing the dialog less than the original size
MINMAXINFO* pMinMaxInfo = reinterpret_cast<MINMAXINFO*>(lParam);
pMinMaxInfo->ptMinTrackSize.x = m_initialWidth;
pMinMaxInfo->ptMinTrackSize.y = m_initialHeight;
}
}
void CPowerRenameUI::_OnSize(_In_ WPARAM wParam)
{
if ((wParam == SIZE_RESTORED || wParam == SIZE_MAXIMIZED) && m_initialWidth)
{
// Calculate window size change delta
RECT rc = { 0 };
GetWindowRect(m_hwnd, &rc);
const int xDelta = RECT_WIDTH(rc) - m_lastWidth;
m_lastWidth += xDelta;
const int yDelta = RECT_HEIGHT(rc) - m_lastHeight;
m_lastHeight += yDelta;
for (UINT u = 0; u < ARRAYSIZE(g_repositionMap); u++)
{
_MoveControl(g_repositionMap[u].id, g_repositionMap[u].flags, xDelta, yDelta);
}
m_listview.OnSize();
}
}
void CPowerRenameUI::_MoveControl(_In_ DWORD id, _In_ DWORD repositionFlags, _In_ int xDelta, _In_ int yDelta)
{
HWND hwnd = GetDlgItem(m_hwnd, id);
UINT flags = SWP_NOOWNERZORDER | SWP_NOZORDER | SWP_NOACTIVATE;
if (!((repositionFlags & Reposition_X) || (repositionFlags & Reposition_Y)))
{
flags |= SWP_NOMOVE;
}
if (!((repositionFlags & Reposition_Width) || (repositionFlags & Reposition_Height)))
{
flags |= SWP_NOSIZE;
}
RECT rcWindow = { 0 };
GetWindowRect(hwnd, &rcWindow);
int cx = RECT_WIDTH(rcWindow);
int cy = RECT_HEIGHT(rcWindow);
MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rcWindow, 2);
int x = rcWindow.left;
int y = rcWindow.top;
if (repositionFlags & Reposition_X)
{
x += xDelta;
}
if (repositionFlags & Reposition_Y)
{
y += yDelta;
}
if (repositionFlags & Reposition_Width)
{
cx += xDelta;
}
if (repositionFlags & Reposition_Height)
{
cy += yDelta;
}
SetWindowPos(hwnd, NULL, x, y, cx, cy, flags);
RedrawWindow(hwnd, NULL, NULL, RDW_INVALIDATE);
}
void CPowerRenameUI::_OnSearchReplaceChanged()
{
// Pass updated search and replace terms to the IPowerRenameRegEx handler
@ -655,7 +827,7 @@ void CPowerRenameListView::Init(_In_ HWND hwndLV)
SetWindowLongPtr(m_hwndLV, GWL_STYLE, dwLVStyle);
// Set the extended view styles
ListView_SetExtendedListViewStyle(m_hwndLV, LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER);
ListView_SetExtendedListViewStyle(m_hwndLV, LVS_EX_CHECKBOXES | LVS_EX_DOUBLEBUFFER | LVS_EX_AUTOSIZECOLUMNS);
// Get the system image lists. Our list view is setup to not destroy
// these since the image list belongs to the entire explorer process
@ -837,6 +1009,14 @@ void CPowerRenameListView::GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout
}
}
void CPowerRenameListView::OnSize()
{
RECT rc = { 0 };
GetClientRect(m_hwndLV, &rc);
ListView_SetColumnWidth(m_hwndLV, 0, RECT_WIDTH(rc) / 2);
ListView_SetColumnWidth(m_hwndLV, 1, RECT_WIDTH(rc) / 2);
}
void CPowerRenameListView::RedrawItems(_In_ int first, _In_ int last)
{
ListView_RedrawItems(m_hwndLV, first, last);

View File

@ -3,7 +3,6 @@
class CPowerRenameListView
{
public:
CPowerRenameListView() = default;
~CPowerRenameListView() = default;
@ -17,6 +16,7 @@ public:
void OnKeyDown(_In_ IPowerRenameManager* psrm, _In_ LV_KEYDOWN* lvKeyDown);
void OnClickList(_In_ IPowerRenameManager* psrm, NM_LISTVIEW* pnmListView);
void GetDisplayInfo(_In_ IPowerRenameManager* psrm, _Inout_ LV_DISPINFO* plvdi);
void OnSize();
HWND GetHWND() { return m_hwndLV; }
private:
@ -45,7 +45,7 @@ public:
IFACEMETHODIMP_(ULONG) Release();
// IPowerRenameUI
IFACEMETHODIMP Show();
IFACEMETHODIMP Show(_In_opt_ HWND hwndParent);
IFACEMETHODIMP Close();
IFACEMETHODIMP Update();
IFACEMETHODIMP get_hwnd(_Out_ HWND* hwnd);
@ -77,6 +77,7 @@ private:
}
HRESULT _DoModal(__in_opt HWND hwnd);
HRESULT _DoModeless(__in_opt HWND hwnd);
static INT_PTR CALLBACK s_DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
@ -90,13 +91,14 @@ private:
return pDlg ? pDlg->_DlgProc(uMsg, wParam, lParam) : FALSE;
}
INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void _OnCommand(_In_ WPARAM wParam, _In_ LPARAM lParam);
BOOL _OnNotify(_In_ WPARAM wParam, _In_ LPARAM lParam);
HRESULT _Initialize(_In_ IPowerRenameManager* psrm, _In_opt_ IDataObject* pdo, _In_ bool enableDragDrop);
void _Cleanup();
INT_PTR _DlgProc(UINT uMsg, WPARAM wParam, LPARAM lParam);
void _OnCommand(_In_ WPARAM wParam, _In_ LPARAM lParam);
BOOL _OnNotify(_In_ WPARAM wParam, _In_ LPARAM lParam);
void _OnSize(_In_ WPARAM wParam);
void _OnGetMinMaxInfo(_In_ LPARAM lParam);
void _OnInitDlg();
void _OnRename();
void _OnAbout();
@ -104,6 +106,7 @@ private:
void _OnDestroyDlg();
void _OnClear();
void _OnSearchReplaceChanged();
void _MoveControl(_In_ DWORD id, _In_ DWORD repositionFlags, _In_ int xDelta, _In_ int yDelta);
HRESULT _ReadSettings();
HRESULT _WriteSettings();
@ -119,6 +122,7 @@ private:
bool m_initialized = false;
bool m_enableDragDrop = false;
bool m_disableCountUpdate = false;
bool m_modeless = true;
HWND m_hwnd = nullptr;
HWND m_hwndLV = nullptr;
HICON m_iconMain = nullptr;
@ -126,6 +130,10 @@ private:
DWORD m_currentRegExId = 0;
UINT m_selectedCount = 0;
UINT m_renamingCount = 0;
int m_initialWidth = 0;
int m_initialHeight = 0;
int m_lastWidth = 0;
int m_lastHeight = 0;
CComPtr<IPowerRenameManager> m_spsrm;
CComPtr<IDataObject> m_spdo;
CComPtr<IDropTargetHelper> m_spdth;

View File

@ -23,17 +23,6 @@
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
/////////////////////////////////////////////////////////////////////////////
//
// Accelerator
//
IDC_SMARTRENAME ACCELERATORS
BEGIN
"?", IDM_ABOUT, ASCII, ALT
"/", IDM_ABOUT, ASCII, ALT
END
/////////////////////////////////////////////////////////////////////////////
//
@ -41,7 +30,7 @@
//
IDD_MAIN DIALOGEX 0, 0, 351, 304
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU
STYLE DS_SETFONT | DS_FIXEDSYS | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
CAPTION "PowerRename"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
@ -49,7 +38,7 @@
EDITTEXT IDC_EDIT_REPLACEWITH,71,38,259,13,ES_AUTOHSCROLL
CONTROL "Use Regular Expressions",IDC_CHECK_USEREGEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,83,93,10
CONTROL "Case Sensitive",IDC_CHECK_CASESENSITIVE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,95,61,10
CONTROL "Match All Occurences",IDC_CHECK_MATCHALLOCCRENCES,
CONTROL "Match All Occurences",IDC_CHECK_MATCHALLOCCURENCES,
"Button",BS_AUTOCHECKBOX | WS_TABSTOP,21,107,85,10
CONTROL "Exclude Files",IDC_CHECK_EXCLUDEFILES,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,132,83,58,10
CONTROL "Exclude Folders",IDC_CHECK_EXCLUDEFOLDERS,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,132,95,68,10
@ -65,9 +54,9 @@
RTEXT "Search for:",IDC_STATIC,25,23,39,8
LTEXT "Replace with:",IDC_STATIC,21,40,43,8
LTEXT "Items Selected: 0 | Renaming: 0",IDC_STATUS_MESSAGE,11,284,137,13
GROUPBOX "Options",IDC_STATIC,11,68,329,58
GROUPBOX "Preview",IDC_STATIC,11,133,329,142
GROUPBOX "Enter the criteria below to rename the items",IDC_STATIC,11,7,329,55
GROUPBOX "Options",IDC_OPTIONSGROUP,11,68,329,58
GROUPBOX "Preview",IDC_PREVIEWGROUP,11,133,329,142
GROUPBOX "Enter the criteria below to rename the items",IDC_SEARCHREPLACEGROUP,11,7,329,55
END

View File

@ -385,6 +385,16 @@ void D2DOverlayWindow::set_theme(const std::wstring& theme) {
}
}
/* Hide the window but do not call on_hide(). Use this to quickly hide the window when needed.
Note, that a proper hide should be made after this before showing the window again.
*/
void D2DOverlayWindow::quick_hide() {
ShowWindow(hwnd, SW_HIDE);
if (thumbnail) {
DwmUnregisterThumbnail(thumbnail);
}
}
float D2DOverlayWindow::get_overlay_opacity() {
return overlay_opacity;
}

View File

@ -47,6 +47,7 @@ public:
~D2DOverlayWindow();
void apply_overlay_opacity(float opacity);
void set_theme(const std::wstring& theme);
void quick_hide();
private:
void animate(int vk_code, int offset);
bool show_thumbnail(const RECT& rect, double alpha);

View File

@ -146,6 +146,10 @@ void OverlayWindow::on_held_press(DWORD vkCode) {
winkey_popup->animate(vkCode);
}
void OverlayWindow::quick_hide() {
winkey_popup->quick_hide();
}
void OverlayWindow::was_hidden() {
target_state->was_hiden();
}

View File

@ -23,6 +23,7 @@ public:
void on_held();
void on_held_press(DWORD vkCode);
void quick_hide();
void was_hidden();
virtual void destroy() override;

View File

@ -11,6 +11,12 @@ bool TargetState::signal_event(unsigned vk_code, bool key_down) {
if (!events.empty() && events.back().key_down == key_down && events.back().vk_code == vk_code) {
return false;
}
// Hide the overlay when WinKey + Shift + S is pressed. 0x53 is the VK code of the S key
if (key_down && state == Shown && vk_code == 0x53 && (GetKeyState(VK_LSHIFT) || GetKeyState(VK_RSHIFT))) {
// We cannot use normal hide() here, there is stuff that needs deinitialization.
// It can be safely done when the user releases the WinKey.
instance->quick_hide();
}
bool supress = false;
if (!key_down && (vk_code == VK_LWIN || vk_code == VK_RWIN) &&
state == Shown &&
@ -23,14 +29,14 @@ bool TargetState::signal_event(unsigned vk_code, bool key_down) {
cv.notify_one();
if (supress) {
// Send a fake key-stroke to prevent the start menu from appearing.
// We use 0x07 VK code, which is undefined. It still prevents the
// We use 0xCF VK code, which is reserved. It still prevents the
// start menu from appearing, but should not interfere with any
// keyboard shortcuts.
INPUT input[3] = { {},{},{} };
input[0].type = INPUT_KEYBOARD;
input[0].ki.wVk = 0x07;
input[0].ki.wVk = 0xCF;
input[1].type = INPUT_KEYBOARD;
input[1].ki.wVk = 0x07;
input[1].ki.wVk = 0xCF;
input[1].ki.dwFlags = KEYEVENTF_KEYUP;
input[2].type = INPUT_KEYBOARD;
input[2].ki.wVk = VK_LWIN;

View File

@ -18,7 +18,16 @@ namespace {
}
}
// Prevent system-wide input lagging while paused in the debugger
//#define DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED
void start_lowlevel_keyboard_hook() {
#if defined(_DEBUG) && defined(DISABLE_LOWLEVEL_KBHOOK_WHEN_DEBUGGED)
if(IsDebuggerPresent()) {
return;
}
#endif
if (!hook_handle) {
hook_handle = SetWindowsHookEx(WH_KEYBOARD_LL, hook_proc, GetModuleHandle(NULL), NULL);
hook_handle_copy = hook_handle;

View File

@ -8,6 +8,8 @@
#include "trace.h"
#include "general_settings.h"
#include <common/dpi_aware.h>
#if _DEBUG && _WIN64
#include "unhandled_exception_handler.h"
#endif
@ -31,6 +33,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
// The app is already running
return 0;
}
DPIAware::EnableDPIAwarenessForThisProcess();
#if _DEBUG && _WIN64
//Global error handlers to diagnose errors.

View File

@ -67,7 +67,7 @@
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
<EnableDpiAwareness>false</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -92,7 +92,7 @@
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
</Link>
<Manifest>
<EnableDpiAwareness>PerMonitorHighDPIAware</EnableDpiAwareness>
<EnableDpiAwareness>false</EnableDpiAwareness>
</Manifest>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -191,7 +191,7 @@ export class GeneralSettings extends React.Component <any, any> {
ref={(input) => {this.theme_reference=input;}}
/>
<Stack>
<Label>Version 0.12.0</Label>
<Label>Version 0.13.0</Label>
<PrimaryButton
styles={{
root: {

File diff suppressed because one or more lines are too long

View File

@ -60,6 +60,7 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<CustomBuildStep>
<Command>
@ -88,6 +89,7 @@
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<AdditionalIncludeDirectories>..;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/Zm150 %(AdditionalOptions)</AdditionalOptions>
</ClCompile>
<Link>
<EnableCOMDATFolding>true</EnableCOMDATFolding>