Compare commits
55 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ac6a013bf5 | ||
|
|
ae9e257e56 | ||
|
|
bb82a9bcf4 | ||
|
|
df61ea0a62 | ||
|
|
feb950be3c | ||
|
|
5b02bda277 | ||
|
|
ce4643d650 | ||
|
|
55c311cd39 | ||
|
|
8ccc996e63 | ||
|
|
bd7150d676 | ||
|
|
48d744b6bd | ||
|
|
6eba8130ba | ||
|
|
5ac7eddd03 | ||
|
|
b5dfe6320d | ||
|
|
0354026292 | ||
|
|
e9ecdb3f56 | ||
|
|
dcee505f6b | ||
|
|
af4678ff18 | ||
|
|
cab5a97117 | ||
|
|
5cfa8889f4 | ||
|
|
0cbe7a16e3 | ||
|
|
aa6cb7d650 | ||
|
|
67e6688e69 | ||
|
|
2077cd4864 | ||
|
|
87fb6fc3d1 | ||
|
|
d584dc5632 | ||
|
|
86704efcec | ||
|
|
6bb0f18d53 | ||
|
|
77e4984468 | ||
|
|
4e18c67ec5 | ||
|
|
41ab94fe13 | ||
|
|
14441ec144 | ||
|
|
f589dd2f26 | ||
|
|
629ba763d7 | ||
|
|
e896e1b3dd | ||
|
|
b92a127200 | ||
|
|
6419c6b1ba | ||
|
|
1f9d9c619e | ||
|
|
c355a2b61e | ||
|
|
17022d50d4 | ||
|
|
fa515604dc | ||
|
|
efb86f453c | ||
|
|
569c18c9f1 | ||
|
|
91223a8431 | ||
|
|
d95e49b535 | ||
|
|
4cb89b3fc2 | ||
|
|
5e56c66201 | ||
|
|
cb454734b0 | ||
|
|
4f29f998c5 | ||
|
|
c241d48df3 | ||
|
|
5ff146bc69 | ||
|
|
de42d9f310 | ||
|
|
f5ed9f1c33 | ||
|
|
01ec472314 | ||
|
|
84ef245b56 |
@@ -46,6 +46,7 @@ build:
|
||||
include:
|
||||
- 'PowerToys.exe'
|
||||
- 'PowerToysSettings.exe'
|
||||
- 'action_runner.exe'
|
||||
- 'modules\FancyZonesEditor.exe'
|
||||
- 'modules\fancyzones.dll'
|
||||
- 'modules\shortcut_guide.dll'
|
||||
|
||||
117
NOTICE.md
@@ -1,28 +1,12 @@
|
||||
# NOTICES AND INFORMATION
|
||||
Do Not Translate or Localize
|
||||
This software incorporates material from third parties.
|
||||
|
||||
This software incorporates material from third parties. Microsoft makes certain
|
||||
open source code available at http://3rdpartysource.microsoft.com, or you may
|
||||
send a check or money order for US $5.00, including the product name, the open
|
||||
source component name, and version number, to:
|
||||
## PowerToy: ImageResizer
|
||||
|
||||
```
|
||||
Source Code Compliance Team
|
||||
Microsoft Corporation
|
||||
One Microsoft Way
|
||||
Redmond, WA 98052
|
||||
USA
|
||||
```
|
||||
|
||||
Notwithstanding any other terms, you may reverse engineer this software to the
|
||||
extent required to debug changes to any libraries licensed under the GNU Lesser
|
||||
General Public License.
|
||||
|
||||
## ImageResizer
|
||||
### Brice Lams's Image Resizer License
|
||||
|
||||
**Source**: https://github.com/bricelam/ImageResizer/
|
||||
|
||||
### License
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) Brice Lambson. All rights reserved.
|
||||
@@ -44,3 +28,98 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
|
||||
## PowerToy: Launcher
|
||||
|
||||
### Wox License
|
||||
|
||||
**Source**: https://github.com/Wox-launcher/Wox
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Wox
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Beta Tadele's Window Walker License
|
||||
|
||||
**Source**: https://github.com/betsegaw/windowwalker
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright 2020 Betsegaw Tadele
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
### Squirrel.Windows License
|
||||
|
||||
**Source**: https://github.com/Squirrel/Squirrel.Windows/
|
||||
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2012 GitHub, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
||||
## PowerToy: PowerRename
|
||||
|
||||
### Chris Davis's SmartRename License
|
||||
|
||||
**Source**: https://github.com/chrdavis/SmartRename
|
||||
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 Chris Davis
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -151,7 +151,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "action_runner", "src\action
|
||||
{17DA04DF-E393-4397-9CF0-84DABE11032E} = {17DA04DF-E393-4397-9CF0-84DABE11032E}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msi_to_msix_upgrade_lib", "src\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "updating", "src\common\updating\updating.vcxproj", "{17DA04DF-E393-4397-9CF0-84DABE11032E}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{E775CC2C-24CB-48D6-9C3A-BE4CCE0DB17A}"
|
||||
EndProject
|
||||
|
||||
@@ -13,7 +13,7 @@ Microsoft PowerToys is a set of utilities for power users to tune and streamline
|
||||
|
||||
### Via Github with MSI [Recommended]
|
||||
|
||||
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.16.0-x64.msi` to download the PowerToys installer.
|
||||
Install from the [Microsoft PowerToys GitHub releases page][github-release-link]. Click on `Assets` to show the files available in the release and then click on `PowerToysSetup-0.16.1-x64.msi` to download the PowerToys installer.
|
||||
|
||||
This is our preferred method.
|
||||
|
||||
|
||||
13
community.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Community
|
||||
|
||||
The PowerToys team is extremely grateful to have the support of an amazing active community. The work you do is incredibly important. PowerToys wouldn’t be near what it is without your help filing bugs, updating documentation, guiding the design, or writing features. We want to say thanks and to recognize your work. This is a living document dedicated to highlighting the high impact community members and their contributions.
|
||||
|
||||
## High impact community members
|
||||
|
||||
### [@Niels9001](https://github.com/niels9001/) - [Niels Laute](https://nielslaute.com/)
|
||||
|
||||
Niels has helped drive large sums of our update toward a new [consistent and modern UX](https://github.com/microsoft/PowerToys/issues/891). This includes the [launcher work](https://github.com/microsoft/PowerToys/issues/44) and [icon design](https://github.com/microsoft/PowerToys/issues/1118).
|
||||
|
||||
### [@riverar](https://github.com/riverar) - [Rafael Rivera](https://withinrafael.com/)
|
||||
|
||||
Rafael has helped do the [upgrade from CppWinRT 1.x to 2.0](https://github.com/microsoft/PowerToys/issues/1907). He directly provided feedback to the CppWinRT team for bugs from this migration as well.
|
||||
6551
doc/images/Logo/AI/PowerToys_UWP_Assets.ai
Normal file
BIN
doc/images/Logo/ICO/PowerToys.ico
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
doc/images/Logo/ICO/PowerToys_14.ico
Normal file
|
After Width: | Height: | Size: 52 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-14-in-16.png
Normal file
|
After Width: | Height: | Size: 454 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-16.png
Normal file
|
After Width: | Height: | Size: 336 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-20.png
Normal file
|
After Width: | Height: | Size: 514 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-24.png
Normal file
|
After Width: | Height: | Size: 637 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-256.png
Normal file
|
After Width: | Height: | Size: 7.1 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-30.png
Normal file
|
After Width: | Height: | Size: 728 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-32.png
Normal file
|
After Width: | Height: | Size: 724 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-36.png
Normal file
|
After Width: | Height: | Size: 812 B |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-40.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-48.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-60.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-64.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-72.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-80.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
doc/images/Logo/PNG/PowerToysAppList.targetsize-96.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
@@ -3,25 +3,17 @@
|
||||
This file captures the prioritized list of issues the FancyZones team will tackle
|
||||
|
||||
## On deck
|
||||
Implement multi-mon editor support [195](https://github.com/microsoft/PowerToys/issues/195)
|
||||
Add telemetry to new editor [196](https://github.com/microsoft/PowerToys/issues/196)
|
||||
|
||||
## Backlog
|
||||
Add tests to the new editor [197](https://github.com/microsoft/PowerToys/issues/197)
|
||||
Hitting Esc while dragging should cancel the drag and not move the window into a zone [170](https://github.com/microsoft/PowerToys/issues/170)
|
||||
Flash Zones is wayyyy too slow with multiple large monitors [167](https://github.com/microsoft/PowerToys/issues/167)
|
||||
Cycle through windows in a Zone [175](https://github.com/microsoft/PowerToys/issues/175)
|
||||
Minimize/restore windows in a zone as a group [174](https://github.com/microsoft/PowerToys/issues/174)
|
||||
FancyZones should support custom layouts for different "environments" [177](https://github.com/microsoft/PowerToys/issues/177)
|
||||
Win+arrow works between monitors [161](https://github.com/microsoft/PowerToys/issues/161)
|
||||
Win+arrow is directional based on zone rect [162](https://github.com/microsoft/PowerToys/issues/162)
|
||||
Dragging a zoned window should restore size to a checkpointed size instead of current rect [166](https://github.com/microsoft/PowerToys/issues/166)
|
||||
FancyZones should merge with MTND and include zone moves in the pop-up [178](https://github.com/microsoft/PowerToys/issues/178)
|
||||
Drag to edge of screen automatically switches virtual desktops [168](https://github.com/microsoft/PowerToys/issues/168)
|
||||
Different color schemes [165](https://github.com/microsoft/PowerToys/issues/165)
|
||||
Ensure you can easily see zone while dragging [163](https://github.com/microsoft/PowerToys/issues/163)
|
||||
Visual updates for Win+Arrow [171](https://github.com/microsoft/PowerToys/issues/171)
|
||||
Add a CLI for FancyZones [180](https://github.com/microsoft/PowerToys/issues/180)
|
||||
Add "magnetic dragging and resizing" mode to FancyZones [181](https://github.com/microsoft/PowerToys/issues/181)
|
||||
Create layout from current windows [159](https://github.com/microsoft/PowerToys/issues/159)
|
||||
Zone sets that have a dynamic number of zones [160](https://github.com/microsoft/PowerToys/issues/160)
|
||||
|
||||
@@ -32,13 +32,15 @@
|
||||
<![CDATA[(WINDOWSBUILDNUMBER >= 17134)]]>
|
||||
</Condition>
|
||||
|
||||
<Icon Id="powertoys.ico" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="powertoys.ico" />
|
||||
<Icon Id="powertoys.exe" SourceFile="$(var.BinX64Dir)\svgs\icon.ico"/>
|
||||
<Property Id="ARPPRODUCTICON" Value="powertoys.exe" />
|
||||
<Feature Id="CoreFeature" Title="PowerToys" AllowAdvertise="no" Absent="disallow" TypicalDefault="install"
|
||||
Description="Contains the Shortcut Guide and Fancy Zones features.">
|
||||
<ComponentGroupRef Id="CoreComponents" />
|
||||
<ComponentGroupRef Id="ResourcesComponents" />
|
||||
</Feature>
|
||||
<SetProperty Id="ARPINSTALLLOCATION" Value="[INSTALLFOLDER]" After="CostFinalize" />
|
||||
|
||||
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER" />
|
||||
<UI>
|
||||
<UIRef Id="WixUI_PTInstallDir"/>
|
||||
@@ -68,9 +70,12 @@
|
||||
<RegistrySearch Id="ExistingExtPath" Root="HKCR" Key="CLSID\{0440049F-D1DC-4E46-B27B-98393D79486B}\InprocServer32" Type="raw"/>
|
||||
</Property>
|
||||
<Property Id ="EXISTINGIMAGERESIZERPATH">
|
||||
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
|
||||
<RegistrySearch Id="ExistingImageResizerPath" Root="HKCR" Key="CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32" Type="raw"/>
|
||||
</Property>
|
||||
|
||||
<InstallUISequence>
|
||||
<Custom Action="DetectPrevInstallPath" After="CostFinalize" />
|
||||
</InstallUISequence>
|
||||
<InstallExecuteSequence>
|
||||
<Custom Action="SetRegisterPowerToysSchTaskParam" Before="RegisterPowerToysSchTask" />
|
||||
<Custom Action="RegisterPowerToysSchTask" After="InstallFiles">
|
||||
@@ -163,6 +168,13 @@
|
||||
DllEntry="TelemetryLogRepairFailCA"
|
||||
/>
|
||||
|
||||
<CustomAction Id="DetectPrevInstallPath"
|
||||
Return="check"
|
||||
Impersonate="yes"
|
||||
BinaryKey="PTCustomActions"
|
||||
DllEntry="DetectPrevInstallPathCA"
|
||||
/>
|
||||
|
||||
<!-- Close 'PowerToys.exe' before uninstall-->
|
||||
<Property Id="MSIRESTARTMANAGERCONTROL" Value="Disable" />
|
||||
<!-- Restart explorer.exe if we detect existing PowerRenameExt.dll or ImageResizerExt.dll installation -->
|
||||
@@ -205,17 +217,15 @@
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder">
|
||||
<Directory Id="ApplicationProgramsFolder" Name="PowerToys"/>
|
||||
</Directory>
|
||||
<Directory Id="ProgramMenuFolder"/>
|
||||
<Directory Id="DesktopFolder" Name="Desktop"/>
|
||||
</Directory>
|
||||
</Directory>
|
||||
</Fragment>
|
||||
|
||||
<Fragment>
|
||||
<DirectoryRef Id="INSTALLFOLDER" FileSource="$(var.BinX64Dir)">
|
||||
<Component Id="powertoys_toast_clsid" Win64="yes">
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}">
|
||||
<RegistryKey Root="HKCR" Key="Software\Classes\CLSID\{DD5CACDA-7C2E-4997-A62A-04A597B58F76}">
|
||||
<RegistryValue Type="string" Value="PowerToys Toast Notifications Background Activator" />
|
||||
<RegistryValue Type="string" Key="LocalServer32" Value="[INSTALLFOLDER]PowerToys.exe -ToastActivated" />
|
||||
<RegistryValue Type="string" Key="LocalServer32" Name="ThreadingModel" Value="Apartment" />
|
||||
@@ -226,9 +236,8 @@
|
||||
<Shortcut Id="ApplicationStartMenuShortcut"
|
||||
Name="PowerToys (Preview)"
|
||||
Description="PowerToys - Windows system utilities to maximize productivity"
|
||||
Directory="ApplicationProgramsFolder"
|
||||
WorkingDirectory="INSTALLFOLDER"
|
||||
Icon="powertoys.ico"
|
||||
Directory="ProgramMenuFolder"
|
||||
Icon="powertoys.exe"
|
||||
IconIndex="0"
|
||||
Advertise="yes">
|
||||
<ShortcutProperty Key="System.AppUserModel.ID" Value="Microsoft.PowerToysWin32"/>
|
||||
@@ -236,7 +245,7 @@
|
||||
</Shortcut>
|
||||
</File>
|
||||
|
||||
<RegistryKey Root="HKCR" Key="powertoys" Action="createAndRemoveOnUninstall">
|
||||
<RegistryKey Root="HKCR" Key="powertoys">
|
||||
<RegistryValue Type="string" Name="URL Protocol" Value=""/>
|
||||
<RegistryValue Type="string" Value="URL:PowerToys custom internal URI protocol"/>
|
||||
<RegistryKey Key="DefaultIcon">
|
||||
@@ -247,7 +256,7 @@
|
||||
</RegistryKey>
|
||||
</RegistryKey>
|
||||
|
||||
<RemoveFolder Id="DeleteShortcutFolder" Directory="ApplicationProgramsFolder" On="uninstall" />
|
||||
|
||||
</Component>
|
||||
|
||||
<Component Id="settings_exe" Guid="A5A461A9-7097-4CBA-9D39-3DBBB6B7B80C" Win64="yes">
|
||||
@@ -256,6 +265,9 @@
|
||||
<Component Id="notifications_dll" Guid="23B25EE4-BCA2-45DF-BBCD-82FBDF01C5AB" Win64="yes">
|
||||
<File Id="Notifications.dll" KeyPath="yes" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="action_runner_exe" Guid="626ABB17-16F0-4007-9A58-6998724A5E14" Win64="yes">
|
||||
<File Id="action_runner.exe" KeyPath="yes" Checksum="yes" />
|
||||
</Component>
|
||||
<Component Id="License_rtf" Guid="3E5AE43B-CFB4-449B-A346-94CAAFF3312E" Win64="yes">
|
||||
<File Source="$(var.RepoDir)\installer\License.rtf" Id="License.rtf" KeyPath="yes" />
|
||||
</Component>
|
||||
@@ -334,68 +346,68 @@
|
||||
<File Source="$(var.BinX64Dir)\modules\ImageResizerExt.dll" KeyPath="yes" />
|
||||
</Component>
|
||||
<Component Id="Module_ImageResizer_Registry" Guid="8B593E2C-2D9B-4EBC-93F7-A2B69707DAC9" Win64="yes">
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{51B4D7E5-7568-4234-B4BB-47FB3C016A69}\InprocServer32">
|
||||
<RegistryValue Value="[ModulesInstallFolder]ImageResizerExt.dll" Type="string" />
|
||||
<RegistryValue Name="ThreadingModel" Value="Apartment" Type="string" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for the drag and drop handler -->
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\Directory\ShellEx\DragDropHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="Directory\ShellEx\DragDropHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<!-- Registry Keys for the context menu handler for each of the following image formats: bmp, dib, gif, jfif, jpe, jpeg, jpg, jxr, png, rle, tif, tiff, wdp -->
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.bmp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.dib\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.gif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.jfif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.jpe\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.jpeg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.jpg\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.jxr\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.png\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.rle\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCR"
|
||||
Key="SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.tif\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.tiff\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
<RegistryValue Root="HKCU"
|
||||
Key="Software\Classes\SystemFileAssociations\.wdp\ShellEx\ContextMenuHandlers\ImageResizer"
|
||||
Value="{51B4D7E5-7568-4234-B4BB-47FB3C016A69}"
|
||||
Type="string" />
|
||||
</Component>
|
||||
<Component Id="Module_PowerPreview" Guid="FF1700D5-1B07-4E07-9A62-4D206645EEA9" Win64="yes">
|
||||
<!-- Component to include PowerPreview Module Source dll's -->
|
||||
@@ -413,7 +425,7 @@
|
||||
<Component Id="Module_PowerPreview_PerUserRegistry" Guid="CD90ADC0-7CD5-4A62-B0AF-23545C1E6DD3" Win64="yes">
|
||||
<!-- Added a separate component for Per-User registry changes -->
|
||||
<!-- Registry Key for Class Registration of Svg Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{ddee2b8a-6807-48a6-bb20-2338174ff779}">
|
||||
<RegistryValue Type="string" Value="SvgPreviewHandler.SvgPreviewHandler" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Svg Preview Handler" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
@@ -430,7 +442,7 @@
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]SvgPreviewHandler.dll" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for Class Registration of Markdown Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
|
||||
<RegistryKey Root="HKCR" Key="CLSID\{45769bcc-e8fd-42d0-947e-02beef77a1f5}">
|
||||
<RegistryValue Type="string" Value="MarkdownPreviewHandler.MarkdownPreviewHandler" />
|
||||
<RegistryValue Type="string" Name="DisplayName" Value="Markdown Preview Handler" />
|
||||
<RegistryValue Type="string" Name="AppID" Value="{CF142243-F059-45AF-8842-DBBE9783DB14}" />
|
||||
@@ -447,27 +459,27 @@
|
||||
<RegistryValue Type="string" Key="InprocServer32\$(var.Version).0" Name="CodeBase" Value="file:///[ModulesInstallFolder]MarkdownPreviewHandler.dll" />
|
||||
</RegistryKey>
|
||||
<!-- Registry Key for AppID registration -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
|
||||
<RegistryKey Root="HKCR" Key="AppID\{CF142243-F059-45AF-8842-DBBE9783DB14}">
|
||||
<RegistryValue Type="expandable" Name="DllSurrogate" Value="%SystemRoot%\system32\prevhost.exe" />
|
||||
</RegistryKey>
|
||||
<!-- Add Svg preview handler to preview handlers list -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{ddee2b8a-6807-48a6-bb20-2338174ff779}" Value="Svg Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add Markdown preview handler to preview handlers list -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Windows\CurrentVersion\PreviewHandlers">
|
||||
<RegistryValue Type="string" Name="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" Value="Markdown Preview Handler" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Svg Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\.svg\shellex">
|
||||
<RegistryKey Root="HKCR" Key=".svg\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{ddee2b8a-6807-48a6-bb20-2338174ff779}" />
|
||||
</RegistryKey>
|
||||
<!-- Add file type association for Markdown Preview Handler -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Classes\.md\shellex">
|
||||
<RegistryKey Root="HKCR" Key=".md\shellex">
|
||||
<RegistryValue Type="string" Key="{8895b1c6-b41f-4c1c-a562-0d564250836f}" Value="{45769bcc-e8fd-42d0-947e-02beef77a1f5}" />
|
||||
</RegistryKey>
|
||||
<!-- Update Key to use IE11 for prevhost.exe -->
|
||||
<RegistryKey Root="HKCU" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
|
||||
<RegistryKey Root="HKLM" Key="Software\Microsoft\Internet Explorer\Main\FeatureControl\FEATURE_BROWSER_EMULATION">
|
||||
<RegistryValue Type="integer" Name="prevhost.exe" Value="11000" />
|
||||
</RegistryKey>
|
||||
</Component>
|
||||
@@ -500,7 +512,7 @@
|
||||
Description="PowerToys - Windows system utilities to maximize productivity"
|
||||
Target="[!PowerToys.exe]"
|
||||
WorkingDirectory="INSTALLFOLDER"
|
||||
Icon="powertoys.ico"
|
||||
Icon="powertoys.exe"
|
||||
Directory="DesktopFolder"/>
|
||||
</Component>
|
||||
</DirectoryRef>
|
||||
@@ -510,6 +522,7 @@
|
||||
<ComponentGroup Id="CoreComponents" Directory="INSTALLFOLDER">
|
||||
<ComponentRef Id="powertoys_exe" />
|
||||
<ComponentRef Id="notifications_dll" />
|
||||
<ComponentRef Id="action_runner_exe" />
|
||||
<ComponentRef Id="powertoys_toast_clsid" />
|
||||
<ComponentRef Id="License_rtf" />
|
||||
<ComponentRef Id="PowerToysSvgs" />
|
||||
|
||||
@@ -2,6 +2,7 @@ LIBRARY "PowerToysSetupCustomActions"
|
||||
|
||||
EXPORTS
|
||||
CreateScheduledTaskCA
|
||||
DetectPrevInstallPathCA
|
||||
RemoveScheduledTasksCA
|
||||
TelemetryLogInstallSuccessCA
|
||||
TelemetryLogInstallCancelCA
|
||||
|
||||
@@ -55,11 +55,13 @@
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
@@ -76,11 +78,13 @@
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<LanguageStandard>stdcpplatest</LanguageStandard>
|
||||
<TreatWarningAsError>true</TreatWarningAsError>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalDependencies>Pathcch.lib;comsupp.lib;taskschd.lib;Secur32.lib;msi.lib;dutil.lib;wcautil.lib;Version.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<AdditionalLibraryDirectories>$(WIX)sdk\$(WixPlatformToolset)\lib\x64;$(SolutionDir)\packages\WiX.3.11.2\tools\sdk\vs2017\lib\x64;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<ModuleDefinitionFile>CustomAction.def</ModuleDefinitionFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
|
||||
@@ -1,13 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files:
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include <msiquery.h>
|
||||
#include <Msi.h>
|
||||
|
||||
// WiX Header Files:
|
||||
#include <wcautil.h>
|
||||
|
||||
#define SECURITY_WIN32
|
||||
#include <Security.h>
|
||||
#include <Lmcons.h>
|
||||
|
||||
// TODO: reference additional headers your program requires here
|
||||
#include <comdef.h>
|
||||
#include <taskschd.h>
|
||||
#include <iostream>
|
||||
#include <strutil.h>
|
||||
#include <string>
|
||||
#include <optional>
|
||||
#include <pathcch.h>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<PropertyGroup>
|
||||
<Version>0.16.1</Version>
|
||||
<Version>0.17.0</Version>
|
||||
<DefineConstants>Version=$(Version);</DefineConstants>
|
||||
</PropertyGroup>
|
||||
</Project>
|
||||
@@ -4,19 +4,24 @@
|
||||
|
||||
#include <string_view>
|
||||
|
||||
#include <common/msi_to_msix_upgrade_lib/msi_to_msix_upgrade.h>
|
||||
#include <common/common.h>
|
||||
#include <common/updating/updating.h>
|
||||
|
||||
#include <winrt/Windows.ApplicationModel.h>
|
||||
#include <winrt/Windows.Storage.h>
|
||||
#include <Msi.h>
|
||||
|
||||
#include "../runner/tray_icon.h"
|
||||
#include "../runner/action_runner_utils.h"
|
||||
|
||||
int uninstall_msi_action()
|
||||
{
|
||||
const auto package_path = get_msi_package_path();
|
||||
const auto package_path = updating::get_msi_package_path();
|
||||
if (package_path.empty())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
if (!uninstall_msi_version(package_path))
|
||||
if (!updating::uninstall_msi_version(package_path))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -33,6 +38,90 @@ int uninstall_msi_action()
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
std::optional<fs::path> copy_self_to_temp_dir()
|
||||
{
|
||||
std::error_code error;
|
||||
auto dst_path = fs::temp_directory_path() / "action_runner.exe";
|
||||
fs::copy_file(get_module_filename(), dst_path, fs::copy_options::overwrite_existing, error);
|
||||
if (error)
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
return std::move(dst_path);
|
||||
}
|
||||
|
||||
bool install_new_version_stage_1(const bool must_restart = false)
|
||||
{
|
||||
std::optional<fs::path> installer;
|
||||
for (auto path : fs::directory_iterator{ updating::get_pending_updates_path() })
|
||||
{
|
||||
if (path.path().native().find(updating::installer_filename_pattern) != std::wstring::npos)
|
||||
{
|
||||
installer.emplace(std::move(path));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!installer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (auto copy_in_temp = copy_self_to_temp_dir())
|
||||
{
|
||||
// detect if PT was running
|
||||
const auto pt_main_window = FindWindowW(pt_tray_icon_window_class, nullptr);
|
||||
const bool launch_powertoys = must_restart || pt_main_window != nullptr;
|
||||
if (pt_main_window != nullptr)
|
||||
{
|
||||
SendMessageW(pt_main_window, WM_CLOSE, 0, 0);
|
||||
}
|
||||
|
||||
std::wstring arguments{ UPDATE_NOW_LAUNCH_STAGE2_CMDARG };
|
||||
arguments += L" \"";
|
||||
arguments += installer->c_str();
|
||||
arguments += L"\" \"";
|
||||
arguments += get_module_folderpath();
|
||||
arguments += L"\" ";
|
||||
arguments += launch_powertoys ? UPDATE_STAGE2_RESTART_PT_CMDARG : UPDATE_STAGE2_DONT_START_PT_CMDARG;
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
||||
sei.lpFile = copy_in_temp->c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
|
||||
sei.lpParameters = arguments.c_str();
|
||||
return ShellExecuteExW(&sei) == TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool install_new_version_stage_2(std::wstring_view installer_path, std::wstring_view install_path, const bool launch_powertoys)
|
||||
{
|
||||
if (MsiInstallProductW(installer_path.data(), nullptr) != ERROR_SUCCESS)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::error_code _;
|
||||
fs::remove(installer_path, _);
|
||||
if (launch_powertoys)
|
||||
{
|
||||
std::wstring new_pt_path{ install_path };
|
||||
new_pt_path += L"\\PowerToys.exe";
|
||||
SHELLEXECUTEINFOW sei{ sizeof(sei) };
|
||||
sei.fMask = { SEE_MASK_FLAG_NO_UI | SEE_MASK_NOASYNC };
|
||||
sei.lpFile = new_pt_path.c_str();
|
||||
sei.nShow = SW_SHOWNORMAL;
|
||||
sei.lpParameters = UPDATE_REPORT_SUCCESS;
|
||||
return ShellExecuteExW(&sei) == TRUE;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
int nArgs = 0;
|
||||
@@ -47,6 +136,19 @@ int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
|
||||
{
|
||||
return uninstall_msi_action();
|
||||
}
|
||||
else if (action == UPDATE_NOW_LAUNCH_STAGE1_CMDARG)
|
||||
{
|
||||
return !install_new_version_stage_1();
|
||||
}
|
||||
else if (action == UPDATE_NOW_LAUNCH_STAGE1_START_PT_CMDARG)
|
||||
{
|
||||
return !install_new_version_stage_1(true);
|
||||
}
|
||||
else if (action == UPDATE_NOW_LAUNCH_STAGE2_CMDARG)
|
||||
{
|
||||
using namespace std::string_view_literals;
|
||||
return !install_new_version_stage_2(args[2], args[3], args[4] == std::wstring_view{ UPDATE_STAGE2_RESTART_PT_CMDARG });
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -157,12 +157,12 @@
|
||||
<ProjectReference Include="..\common\common.vcxproj">
|
||||
<Project>{74485049-c722-400f-abe5-86ac52d929b3}</Project>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\common\msi_to_msix_upgrade_lib\msi_to_msix_upgrade_lib.vcxproj">
|
||||
<ProjectReference Include="..\common\updating\updating.vcxproj">
|
||||
<Project>{17da04df-e393-4397-9cf0-84dabe11032e}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\runner\msi_to_msix_upgrade.h" />
|
||||
<ClInclude Include="..\runner\updating.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
|
||||
@@ -27,3 +27,14 @@ VersionHelper::VersionHelper(int major, int minor, int revision) :
|
||||
revision(revision)
|
||||
{
|
||||
}
|
||||
|
||||
std::wstring VersionHelper::toWstring() const
|
||||
{
|
||||
std::wstring result{ L"v" };
|
||||
result += std::to_wstring(major);
|
||||
result += L'.';
|
||||
result += std::to_wstring(minor);
|
||||
result += L'.';
|
||||
result += std::to_wstring(revision);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -13,4 +13,6 @@ struct VersionHelper
|
||||
int major;
|
||||
int minor;
|
||||
int revision;
|
||||
|
||||
std::wstring toWstring() const;
|
||||
};
|
||||
|
||||
@@ -364,27 +364,31 @@ WindowState get_window_state(HWND hwnd)
|
||||
return RESTORED;
|
||||
}
|
||||
|
||||
bool is_process_elevated()
|
||||
bool is_process_elevated(const bool use_cached_value)
|
||||
{
|
||||
HANDLE token = nullptr;
|
||||
bool elevated = false;
|
||||
auto detection_func = []() {
|
||||
HANDLE token = nullptr;
|
||||
bool elevated = false;
|
||||
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
||||
{
|
||||
TOKEN_ELEVATION elevation;
|
||||
DWORD size;
|
||||
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
|
||||
if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
||||
{
|
||||
elevated = (elevation.TokenIsElevated != 0);
|
||||
TOKEN_ELEVATION elevation;
|
||||
DWORD size;
|
||||
if (GetTokenInformation(token, TokenElevation, &elevation, sizeof(elevation), &size))
|
||||
{
|
||||
elevated = (elevation.TokenIsElevated != 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (token)
|
||||
{
|
||||
CloseHandle(token);
|
||||
}
|
||||
if (token)
|
||||
{
|
||||
CloseHandle(token);
|
||||
}
|
||||
|
||||
return elevated;
|
||||
return elevated;
|
||||
};
|
||||
static const bool cached_value = detection_func();
|
||||
return use_cached_value ? cached_value : detection_func();
|
||||
}
|
||||
|
||||
bool drop_elevated_privileges()
|
||||
|
||||
@@ -61,7 +61,7 @@ enum WindowState
|
||||
WindowState get_window_state(HWND hwnd);
|
||||
|
||||
// Returns true if the current process is running with elevated privileges
|
||||
bool is_process_elevated();
|
||||
bool is_process_elevated(const bool use_cached_value = true);
|
||||
|
||||
// Drops the elevated privilages if present
|
||||
bool drop_elevated_privileges();
|
||||
@@ -78,7 +78,7 @@ bool run_same_elevation(const std::wstring& file, const std::wstring& params);
|
||||
// Returns true if the current process is running from administrator account
|
||||
bool check_user_is_admin();
|
||||
|
||||
//Returns true when one or more strings from vector found in string
|
||||
// Returns true when one or more strings from vector found in string
|
||||
bool find_app_name_in_path(const std::wstring& where, const std::vector<std::wstring>& what);
|
||||
|
||||
// Get the executable path or module name for modern apps
|
||||
|
||||
@@ -162,6 +162,7 @@
|
||||
<ClCompile Include="start_visible.cpp" />
|
||||
<ClCompile Include="tasklist_positions.cpp" />
|
||||
<ClCompile Include="common.cpp" />
|
||||
<ClCompile Include="version.cpp" />
|
||||
<ClCompile Include="VersionHelper.cpp" />
|
||||
<ClCompile Include="windows_colors.cpp" />
|
||||
<ClCompile Include="window_helpers.cpp" />
|
||||
|
||||
@@ -159,5 +159,11 @@
|
||||
<ClCompile Include="VersionHelper.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="version.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,8 +1,9 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "common.h"
|
||||
#include "monitors.h"
|
||||
|
||||
#include "common.h"
|
||||
|
||||
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
|
||||
{
|
||||
auto lhs_tuple = std::make_tuple(lhs.rect.left, lhs.rect.right, lhs.rect.top, lhs.rect.bottom);
|
||||
@@ -10,43 +11,47 @@ bool operator==(const ScreenSize& lhs, const ScreenSize& rhs)
|
||||
return lhs_tuple == rhs_tuple;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK get_displays_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
static BOOL CALLBACK GetDisplaysEnumCb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
{
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcWork);
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
if (GetMonitorInfo(monitor, &monitorInfo))
|
||||
{
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitorInfo.rcWork);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
static BOOL CALLBACK get_displays_enum_cb_with_toolbar(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
static BOOL CALLBACK GetDisplaysEnumCbWithNonWorkingArea(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
{
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitor_info.rcMonitor);
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
if (GetMonitorInfo(monitor, &monitorInfo))
|
||||
{
|
||||
reinterpret_cast<std::vector<MonitorInfo>*>(data)->emplace_back(monitor, monitorInfo.rcMonitor);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool include_toolbar)
|
||||
std::vector<MonitorInfo> MonitorInfo::GetMonitors(bool includeNonWorkingArea)
|
||||
{
|
||||
std::vector<MonitorInfo> monitors;
|
||||
EnumDisplayMonitors(NULL, NULL, include_toolbar ? get_displays_enum_cb_with_toolbar : get_displays_enum_cb, reinterpret_cast<LPARAM>(&monitors));
|
||||
EnumDisplayMonitors(NULL, NULL, includeNonWorkingArea ? GetDisplaysEnumCbWithNonWorkingArea : GetDisplaysEnumCb, reinterpret_cast<LPARAM>(&monitors));
|
||||
std::sort(begin(monitors), end(monitors), [](const MonitorInfo& lhs, const MonitorInfo& rhs) {
|
||||
return lhs.rect < rhs.rect;
|
||||
});
|
||||
return monitors;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
static BOOL CALLBACK GetPrimaryDisplayEnumCb(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
{
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
if (monitor_info.dwFlags & MONITORINFOF_PRIMARY)
|
||||
MONITORINFOEX monitorInfo;
|
||||
monitorInfo.cbSize = sizeof(MONITORINFOEX);
|
||||
|
||||
if (GetMonitorInfo(monitor, &monitorInfo) && (monitorInfo.dwFlags & MONITORINFOF_PRIMARY))
|
||||
{
|
||||
reinterpret_cast<MonitorInfo*>(data)->handle = monitor;
|
||||
reinterpret_cast<MonitorInfo*>(data)->rect = monitor_info.rcWork;
|
||||
reinterpret_cast<MonitorInfo*>(data)->rect = monitorInfo.rcWork;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
@@ -54,26 +59,6 @@ static BOOL CALLBACK get_primary_display_enum_cb(HMONITOR monitor, HDC hdc, LPRE
|
||||
MonitorInfo MonitorInfo::GetPrimaryMonitor()
|
||||
{
|
||||
MonitorInfo primary({}, {});
|
||||
EnumDisplayMonitors(NULL, NULL, get_primary_display_enum_cb, reinterpret_cast<LPARAM>(&primary));
|
||||
EnumDisplayMonitors(NULL, NULL, GetPrimaryDisplayEnumCb, reinterpret_cast<LPARAM>(&primary));
|
||||
return primary;
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromWindow(HWND hwnd)
|
||||
{
|
||||
auto monitor = MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST);
|
||||
return GetFromHandle(monitor);
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromPoint(POINT p)
|
||||
{
|
||||
auto monitor = MonitorFromPoint(p, MONITOR_DEFAULTTONEAREST);
|
||||
return GetFromHandle(monitor);
|
||||
}
|
||||
|
||||
MonitorInfo MonitorInfo::GetFromHandle(HMONITOR monitor)
|
||||
{
|
||||
MONITORINFOEX monitor_info;
|
||||
monitor_info.cbSize = sizeof(MONITORINFOEX);
|
||||
GetMonitorInfo(monitor, &monitor_info);
|
||||
return MonitorInfo(monitor, monitor_info.rcWork);
|
||||
}
|
||||
|
||||
@@ -31,15 +31,8 @@ struct MonitorInfo : ScreenSize
|
||||
HMONITOR handle;
|
||||
|
||||
// Returns monitor rects ordered from left to right
|
||||
static std::vector<MonitorInfo> GetMonitors(bool include_toolbar);
|
||||
// Return primary display
|
||||
static std::vector<MonitorInfo> GetMonitors(bool includeNonWorkingArea);
|
||||
static MonitorInfo GetPrimaryMonitor();
|
||||
// Return monitor on which hwnd window is displayed
|
||||
static MonitorInfo GetFromWindow(HWND hwnd);
|
||||
// Return monitor nearest to a point
|
||||
static MonitorInfo GetFromPoint(POINT p);
|
||||
// Return monitor info given a HMONITOR
|
||||
static MonitorInfo GetFromHandle(HMONITOR monitor);
|
||||
};
|
||||
|
||||
bool operator==(const ScreenSize& lhs, const ScreenSize& rhs);
|
||||
|
||||
@@ -1,157 +0,0 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "msi_to_msix_upgrade.h"
|
||||
|
||||
#include <msi.h>
|
||||
#include <common/common.h>
|
||||
#include <common/json.h>
|
||||
|
||||
#include <common/winstore.h>
|
||||
#include <common/notifications.h>
|
||||
#include <MsiQuery.h>
|
||||
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
#include <winrt/Windows.Web.Http.Headers.h>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
|
||||
#include "VersionHelper.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t* POWER_TOYS_UPGRADE_CODE = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
||||
const wchar_t* DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH = L"delete_previous_powertoys_confirm";
|
||||
const wchar_t* USER_AGENT = L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
|
||||
const wchar_t* LATEST_RELEASE_ENDPOINT = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
|
||||
const wchar_t* MSIX_PACKAGE_NAME = L"Microsoft.PowerToys";
|
||||
const wchar_t* MSIX_PACKAGE_PUBLISHER = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
const wchar_t* OFFER_UNINSTALL_MSI = L"We've detected a previous installation of PowerToys. Would you like to remove it?";
|
||||
const wchar_t* OFFER_UNINSTALL_MSI_TITLE = L"PowerToys: uninstall previous version?";
|
||||
const wchar_t* UNINSTALLATION_SUCCESS = L"Previous version of PowerToys was uninstalled successfully.";
|
||||
const wchar_t* UNINSTALLATION_UNKNOWN_ERROR = L"Error: please uninstall the previous version of PowerToys manually.";
|
||||
}
|
||||
|
||||
std::wstring get_msi_package_path()
|
||||
{
|
||||
std::wstring package_path;
|
||||
wchar_t GUID_product_string[39];
|
||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, GUID_product_string); !found)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(GUID_product_string); !installed)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
DWORD package_path_size = 0;
|
||||
|
||||
if (const bool has_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size); !has_package_path)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
package_path = std::wstring(++package_path_size, L'\0');
|
||||
if (const bool got_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size); !got_package_path)
|
||||
{
|
||||
package_path = {};
|
||||
return package_path;
|
||||
}
|
||||
|
||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||
|
||||
return package_path;
|
||||
}
|
||||
|
||||
bool offer_msi_uninstallation()
|
||||
{
|
||||
const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH);
|
||||
return selection == IDYES;
|
||||
}
|
||||
|
||||
bool uninstall_msi_version(const std::wstring& package_path)
|
||||
{
|
||||
const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
|
||||
if (ERROR_SUCCESS == uninstall_result)
|
||||
{
|
||||
notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS);
|
||||
return true;
|
||||
}
|
||||
else if (auto system_message = get_last_error_message(uninstall_result); system_message.has_value())
|
||||
{
|
||||
try
|
||||
{
|
||||
notifications::show_toast(*system_message);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::future<std::optional<new_version_download_info>> check_for_new_github_release_async()
|
||||
{
|
||||
try
|
||||
{
|
||||
winrt::Windows::Web::Http::HttpClient client;
|
||||
auto headers = client.DefaultRequestHeaders();
|
||||
headers.UserAgent().TryParseAdd(USER_AGENT);
|
||||
|
||||
auto response = co_await client.GetAsync(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
const auto body = co_await response.Content().ReadAsStringAsync();
|
||||
auto json_body = json::JsonValue::Parse(body).GetObjectW();
|
||||
auto new_version = json_body.GetNamedString(L"tag_name");
|
||||
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
|
||||
|
||||
VersionHelper github_version(winrt::to_string(new_version));
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
if (github_version > current_version)
|
||||
{
|
||||
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str() };
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async()
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager package_manager;
|
||||
|
||||
try
|
||||
{
|
||||
auto packages = package_manager.FindPackagesForUser({}, MSIX_PACKAGE_NAME, MSIX_PACKAGE_PUBLISHER);
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
for (auto package : packages)
|
||||
{
|
||||
VersionHelper msix_version(package.Id().Version().Major, package.Id().Version().Minor, package.Id().Version().Revision);
|
||||
|
||||
if (msix_version < current_version)
|
||||
{
|
||||
co_await package_manager.RemovePackageAsync(package.Id().FullName());
|
||||
co_return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
co_return false;
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <future>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
std::wstring get_msi_package_path();
|
||||
bool uninstall_msi_version(const std::wstring& package_path);
|
||||
bool offer_msi_uninstallation();
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async();
|
||||
|
||||
struct new_version_download_info
|
||||
{
|
||||
winrt::Windows::Foundation::Uri release_page_uri;
|
||||
std::wstring version_string;
|
||||
};
|
||||
std::future<std::optional<new_version_download_info>> check_for_new_github_release_async();
|
||||
@@ -1,13 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
#pragma warning (disable: 5205)
|
||||
#include <winrt/base.h>
|
||||
#pragma warning (default: 5205)
|
||||
#include <Windows.h>
|
||||
#include <MsiQuery.h>
|
||||
#include <Shlwapi.h>
|
||||
|
||||
#endif //PCH_H
|
||||
@@ -33,6 +33,11 @@ namespace
|
||||
constexpr std::wstring_view WIN32_AUMID = L"Microsoft.PowerToysWin32";
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
constexpr std::wstring_view SNOOZE_BUTTON = L"Snooze";
|
||||
}
|
||||
|
||||
static DWORD loop_thread_id()
|
||||
{
|
||||
static const DWORD thread_id = GetCurrentThreadId();
|
||||
@@ -213,7 +218,14 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
|
||||
toast_xml += selection_id;
|
||||
toast_xml += LR"(" type="selection" defaultInput=")";
|
||||
toast_xml += std::to_wstring(b.durations[0].minutes);
|
||||
toast_xml += LR"(">)";
|
||||
toast_xml += L'"';
|
||||
if (!b.snooze_title.empty())
|
||||
{
|
||||
toast_xml += LR"( title=")";
|
||||
toast_xml += b.snooze_title;
|
||||
toast_xml += L'"';
|
||||
}
|
||||
toast_xml += L'>';
|
||||
for (const auto& duration : b.durations)
|
||||
{
|
||||
toast_xml += LR"(<selection id=")";
|
||||
@@ -269,7 +281,9 @@ void notifications::show_toast_with_activations(std::wstring message, std::wstri
|
||||
toast_xml += selection_id;
|
||||
toast_xml += '"';
|
||||
}
|
||||
toast_xml += LR"( content="" />)";
|
||||
toast_xml += LR"( content=")";
|
||||
toast_xml += localized_strings::SNOOZE_BUTTON;
|
||||
toast_xml += LR"(" />)";
|
||||
} },
|
||||
actions[i]);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ namespace notifications
|
||||
|
||||
struct snooze_button
|
||||
{
|
||||
std::wstring snooze_title;
|
||||
std::vector<snooze_duration> durations;
|
||||
};
|
||||
|
||||
|
||||
17
src/common/updating/pch.h
Normal file
@@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
#pragma warning(disable : 5205)
|
||||
#include <winrt/base.h>
|
||||
#pragma warning(default : 5205)
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#include <Windows.h>
|
||||
#include <MsiQuery.h>
|
||||
#include <Shlwapi.h>
|
||||
#include <Shobjidl.h>
|
||||
#include <Knownfolders.h>
|
||||
#include <ShlObj_core.h>
|
||||
|
||||
#endif //PCH_H
|
||||
287
src/common/updating/updating.cpp
Normal file
@@ -0,0 +1,287 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "version.h"
|
||||
|
||||
#include "updating.h"
|
||||
|
||||
#include <msi.h>
|
||||
#include <common/common.h>
|
||||
#include <common/json.h>
|
||||
#include <common/version.h>
|
||||
#include <common/settings_helpers.h>
|
||||
#include <common/winstore.h>
|
||||
#include <common/notifications.h>
|
||||
|
||||
#include <winrt/Windows.Web.Http.h>
|
||||
#include <winrt/Windows.Web.Http.Headers.h>
|
||||
#include <winrt/Windows.Storage.Streams.h>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
#include <winrt/Windows.Networking.Connectivity.h>
|
||||
|
||||
#include "VersionHelper.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
const wchar_t POWER_TOYS_UPGRADE_CODE[] = L"{42B84BF7-5FBF-473B-9C8B-049DC16F7708}";
|
||||
const wchar_t DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH[] = L"delete_previous_powertoys_confirm";
|
||||
const wchar_t USER_AGENT[] = L"Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.2; WOW64; Trident/6.0)";
|
||||
const wchar_t LATEST_RELEASE_ENDPOINT[] = L"https://api.github.com/repos/microsoft/PowerToys/releases/latest";
|
||||
const wchar_t MSIX_PACKAGE_NAME[] = L"Microsoft.PowerToys";
|
||||
const wchar_t MSIX_PACKAGE_PUBLISHER[] = L"CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US";
|
||||
|
||||
const wchar_t UPDATE_NOTIFY_TOAST_TAG[] = L"PTUpdateNotifyTag";
|
||||
const wchar_t UPDATE_READY_TOAST_TAG[] = L"PTUpdateReadyTag";
|
||||
const size_t MAX_DOWNLOAD_ATTEMPTS = 3;
|
||||
}
|
||||
|
||||
namespace localized_strings
|
||||
{
|
||||
const wchar_t OFFER_UNINSTALL_MSI[] = L"We've detected a previous installation of PowerToys. Would you like to remove it?";
|
||||
const wchar_t OFFER_UNINSTALL_MSI_TITLE[] = L"PowerToys: uninstall previous version?";
|
||||
const wchar_t UNINSTALLATION_SUCCESS[] = L"Previous version of PowerToys was uninstalled successfully.";
|
||||
const wchar_t UNINSTALLATION_UNKNOWN_ERROR[] = L"Error: please uninstall the previous version of PowerToys manually.";
|
||||
|
||||
const wchar_t GITHUB_NEW_VERSION_READY_TO_INSTALL[] = L"An update to PowerToys is ready to install.\n";
|
||||
const wchar_t GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR[] = L"Error: couldn't download PowerToys installer. Visit our GitHub page to update.\n";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_NOW[] = L"Update now";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART[] = L"At next launch";
|
||||
|
||||
const wchar_t GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT[] = L"An update to PowerToys is available. Visit our GitHub page to update.\n";
|
||||
const wchar_t GITHUB_NEW_VERSION_AGREE[] = L"Visit";
|
||||
const wchar_t GITHUB_NEW_VERSION_SNOOZE_TITLE[] = L"Click Snooze to be reminded in:";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D[] = L"1 day";
|
||||
const wchar_t GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D[] = L"5 days";
|
||||
}
|
||||
namespace updating
|
||||
{
|
||||
inline winrt::Windows::Web::Http::HttpClient create_http_client()
|
||||
{
|
||||
winrt::Windows::Web::Http::HttpClient client;
|
||||
auto headers = client.DefaultRequestHeaders();
|
||||
headers.UserAgent().TryParseAdd(USER_AGENT);
|
||||
return client;
|
||||
}
|
||||
|
||||
std::wstring get_msi_package_path()
|
||||
{
|
||||
std::wstring package_path;
|
||||
wchar_t GUID_product_string[39];
|
||||
if (const bool found = ERROR_SUCCESS == MsiEnumRelatedProductsW(POWER_TOYS_UPGRADE_CODE, 0, 0, GUID_product_string); !found)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
if (const bool installed = INSTALLSTATE_DEFAULT == MsiQueryProductStateW(GUID_product_string); !installed)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
DWORD package_path_size = 0;
|
||||
|
||||
if (const bool has_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, nullptr, &package_path_size); !has_package_path)
|
||||
{
|
||||
return package_path;
|
||||
}
|
||||
|
||||
package_path = std::wstring(++package_path_size, L'\0');
|
||||
if (const bool got_package_path = ERROR_SUCCESS == MsiGetProductInfoW(GUID_product_string, INSTALLPROPERTY_LOCALPACKAGE, package_path.data(), &package_path_size); !got_package_path)
|
||||
{
|
||||
package_path = {};
|
||||
return package_path;
|
||||
}
|
||||
|
||||
package_path.resize(size(package_path) - 1); // trim additional \0 which we got from MsiGetProductInfoW
|
||||
|
||||
return package_path;
|
||||
}
|
||||
|
||||
bool offer_msi_uninstallation()
|
||||
{
|
||||
const auto selection = SHMessageBoxCheckW(nullptr, localized_strings::OFFER_UNINSTALL_MSI, localized_strings::OFFER_UNINSTALL_MSI_TITLE, MB_ICONQUESTION | MB_YESNO, IDNO, DONT_SHOW_AGAIN_RECORD_REGISTRY_PATH);
|
||||
return selection == IDYES;
|
||||
}
|
||||
|
||||
bool uninstall_msi_version(const std::wstring& package_path)
|
||||
{
|
||||
const auto uninstall_result = MsiInstallProductW(package_path.c_str(), L"REMOVE=ALL");
|
||||
if (ERROR_SUCCESS == uninstall_result)
|
||||
{
|
||||
notifications::show_toast(localized_strings::UNINSTALLATION_SUCCESS);
|
||||
return true;
|
||||
}
|
||||
else if (auto system_message = get_last_error_message(uninstall_result); system_message.has_value())
|
||||
{
|
||||
try
|
||||
{
|
||||
notifications::show_toast(*system_message);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
notifications::show_toast(localized_strings::UNINSTALLATION_UNKNOWN_ERROR);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async()
|
||||
{
|
||||
try
|
||||
{
|
||||
auto client = create_http_client();
|
||||
auto response = co_await client.GetAsync(winrt::Windows::Foundation::Uri{ LATEST_RELEASE_ENDPOINT });
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
const auto body = co_await response.Content().ReadAsStringAsync();
|
||||
auto json_body = json::JsonValue::Parse(body).GetObjectW();
|
||||
auto new_version = json_body.GetNamedString(L"tag_name");
|
||||
winrt::Windows::Foundation::Uri release_page_uri{ json_body.GetNamedString(L"html_url") };
|
||||
|
||||
VersionHelper github_version(winrt::to_string(new_version));
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
if (github_version > current_version)
|
||||
{
|
||||
const std::wstring_view required_asset_extension = winstore::running_as_packaged() ? L".msix" : L".msi";
|
||||
const std::wstring_view required_architecture = get_architecture_string(get_current_architecture());
|
||||
constexpr const std::wstring_view required_filename_pattern = updating::installer_filename_pattern;
|
||||
for (auto asset_elem : json_body.GetNamedArray(L"assets"))
|
||||
{
|
||||
auto asset{ asset_elem.GetObjectW() };
|
||||
std::wstring filename_lower = asset.GetNamedString(L"name", {}).c_str();
|
||||
std::transform(begin(filename_lower), end(filename_lower), begin(filename_lower), ::towlower);
|
||||
|
||||
const bool extension_matched = filename_lower.ends_with(required_asset_extension);
|
||||
const bool architecture_matched = filename_lower.find(required_architecture) != std::wstring::npos;
|
||||
const bool filename_matched = filename_lower.find(required_filename_pattern) != std::wstring::npos;
|
||||
if (extension_matched && architecture_matched && filename_matched)
|
||||
{
|
||||
winrt::Windows::Foundation::Uri msi_download_url{ asset.GetNamedString(L"browser_download_url") };
|
||||
co_return new_version_download_info{ std::move(release_page_uri), new_version.c_str(), std::move(msi_download_url), std::move(filename_lower) };
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
co_return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async()
|
||||
{
|
||||
winrt::Windows::Management::Deployment::PackageManager package_manager;
|
||||
|
||||
try
|
||||
{
|
||||
auto packages = package_manager.FindPackagesForUser({}, MSIX_PACKAGE_NAME, MSIX_PACKAGE_PUBLISHER);
|
||||
VersionHelper current_version(VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION);
|
||||
|
||||
for (auto package : packages)
|
||||
{
|
||||
VersionHelper msix_version(package.Id().Version().Major, package.Id().Version().Minor, package.Id().Version().Revision);
|
||||
|
||||
if (msix_version < current_version)
|
||||
{
|
||||
co_await package_manager.RemovePackageAsync(package.Id().FullName());
|
||||
co_return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
}
|
||||
co_return false;
|
||||
}
|
||||
|
||||
bool could_be_costly_connection()
|
||||
{
|
||||
using namespace winrt::Windows::Networking::Connectivity;
|
||||
ConnectionProfile internetConnectionProfile = NetworkInformation::GetInternetConnectionProfile();
|
||||
return internetConnectionProfile.IsWwanConnectionProfile();
|
||||
}
|
||||
|
||||
std::filesystem::path get_pending_updates_path()
|
||||
{
|
||||
auto path_str{ PTSettingsHelper::get_root_save_folder_location() };
|
||||
path_str += L"\\Updates";
|
||||
return { std::move(path_str) };
|
||||
}
|
||||
|
||||
std::future<void> attempt_to_download_installer(const std::filesystem::path& destination, const winrt::Windows::Foundation::Uri& url)
|
||||
{
|
||||
namespace storage = winrt::Windows::Storage;
|
||||
|
||||
auto client = create_http_client();
|
||||
auto response = co_await client.GetAsync(url);
|
||||
(void)response.EnsureSuccessStatusCode();
|
||||
auto msi_installer_file_stream = co_await storage::Streams::FileRandomAccessStream::OpenAsync(destination.c_str(), storage::FileAccessMode::ReadWrite, storage::StorageOpenOptions::AllowReadersAndWriters, storage::Streams::FileOpenDisposition::CreateAlways);
|
||||
co_await response.Content().WriteToStreamAsync(msi_installer_file_stream);
|
||||
}
|
||||
|
||||
std::future<void> try_autoupdate(const bool download_updates_automatically)
|
||||
{
|
||||
const auto new_version = co_await get_new_github_version_info_async();
|
||||
if (!new_version)
|
||||
{
|
||||
co_return;
|
||||
}
|
||||
using namespace localized_strings;
|
||||
auto current_version_to_next_version = VersionHelper{ VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION }.toWstring();
|
||||
current_version_to_next_version += L" -> ";
|
||||
current_version_to_next_version += new_version->version_string;
|
||||
|
||||
if (download_updates_automatically && !could_be_costly_connection())
|
||||
{
|
||||
auto installer_download_dst = get_pending_updates_path();
|
||||
std::error_code _;
|
||||
std::filesystem::create_directories(installer_download_dst, _);
|
||||
installer_download_dst /= new_version->msi_filename;
|
||||
|
||||
bool download_success = false;
|
||||
for (size_t i = 0; i < MAX_DOWNLOAD_ATTEMPTS; ++i)
|
||||
{
|
||||
try
|
||||
{
|
||||
co_await attempt_to_download_installer(installer_download_dst, new_version->msi_download_url);
|
||||
download_success = true;
|
||||
break;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// reattempt to download or do nothing
|
||||
}
|
||||
}
|
||||
if (!download_success)
|
||||
{
|
||||
notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
|
||||
std::wstring contents = GITHUB_NEW_VERSION_DOWNLOAD_INSTALL_ERROR;
|
||||
contents += current_version_to_next_version;
|
||||
|
||||
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
|
||||
|
||||
co_return;
|
||||
}
|
||||
|
||||
notifications::toast_params toast_params{ UPDATE_READY_TOAST_TAG, false };
|
||||
std::wstring new_version_ready{ GITHUB_NEW_VERSION_READY_TO_INSTALL };
|
||||
new_version_ready += current_version_to_next_version;
|
||||
|
||||
notifications::show_toast_with_activations(std::move(new_version_ready),
|
||||
{},
|
||||
{ notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_NOW, L"powertoys://update_now/" },
|
||||
notifications::link_button{ GITHUB_NEW_VERSION_UPDATE_AFTER_RESTART, L"powertoys://schedule_update/" },
|
||||
notifications::snooze_button{ GITHUB_NEW_VERSION_SNOOZE_TITLE, { { GITHUB_NEW_VERSION_UPDATE_SNOOZE_1D, 24 * 60 }, { GITHUB_NEW_VERSION_UPDATE_SNOOZE_5D, 120 * 60 } } } },
|
||||
std::move(toast_params));
|
||||
}
|
||||
else
|
||||
{
|
||||
notifications::toast_params toast_params{ UPDATE_NOTIFY_TOAST_TAG, false };
|
||||
std::wstring contents = GITHUB_NEW_VERSION_AVAILABLE_OFFER_VISIT;
|
||||
contents += current_version_to_next_version;
|
||||
notifications::show_toast_with_activations(std::move(contents), {}, { notifications::link_button{ GITHUB_NEW_VERSION_AGREE, new_version->release_page_uri.ToString().c_str() } }, std::move(toast_params));
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/common/updating/updating.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <future>
|
||||
#include <filesystem>
|
||||
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
|
||||
namespace updating
|
||||
{
|
||||
std::wstring get_msi_package_path();
|
||||
bool uninstall_msi_version(const std::wstring& package_path);
|
||||
bool offer_msi_uninstallation();
|
||||
|
||||
std::future<bool> uninstall_previous_msix_version_async();
|
||||
|
||||
struct new_version_download_info
|
||||
{
|
||||
winrt::Windows::Foundation::Uri release_page_uri;
|
||||
std::wstring version_string;
|
||||
winrt::Windows::Foundation::Uri msi_download_url;
|
||||
std::wstring msi_filename;
|
||||
};
|
||||
|
||||
std::future<std::optional<new_version_download_info>> get_new_github_version_info_async();
|
||||
std::future<void> try_autoupdate(const bool download_updates_automatically);
|
||||
std::filesystem::path get_pending_updates_path();
|
||||
|
||||
constexpr inline std::wstring_view installer_filename_pattern = L"powertoyssetup";
|
||||
}
|
||||
@@ -22,8 +22,9 @@
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{17DA04DF-E393-4397-9CF0-84DABE11032E}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>msitomsixupgradelib</RootNamespace>
|
||||
<RootNamespace>updating</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
<ProjectName>updating</ProjectName>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
@@ -171,11 +172,11 @@
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="msi_to_msix_upgrade.h" />
|
||||
<ClInclude Include="updating.h" />
|
||||
<ClInclude Include="pch.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="msi_to_msix_upgrade.cpp" />
|
||||
<ClCompile Include="updating.cpp" />
|
||||
<ClCompile Include="pch.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
@@ -18,7 +18,7 @@
|
||||
<ClInclude Include="pch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msi_to_msix_upgrade.h">
|
||||
<ClInclude Include="updating.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
@@ -26,7 +26,7 @@
|
||||
<ClCompile Include="pch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msi_to_msix_upgrade.cpp">
|
||||
<ClCompile Include="updating.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
21
src/common/version.cpp
Normal file
@@ -0,0 +1,21 @@
|
||||
#include "pch.h"
|
||||
#include "version.h"
|
||||
|
||||
version_architecture get_current_architecture()
|
||||
{
|
||||
// TODO: detect ARM build with #ifdef
|
||||
return version_architecture::x64;
|
||||
}
|
||||
|
||||
const wchar_t* get_architecture_string(const version_architecture v)
|
||||
{
|
||||
switch (v)
|
||||
{
|
||||
case version_architecture::x64:
|
||||
return L"x64";
|
||||
case version_architecture::arm:
|
||||
return L"arm";
|
||||
default:
|
||||
throw std::runtime_error("unknown architecture");
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
#define STRINGIZE2(s) #s
|
||||
#define STRINGIZE(s) STRINGIZE2(s)
|
||||
|
||||
|
||||
#include "Generated Files\version_gen.h"
|
||||
|
||||
#define FILE_VERSION VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, 0
|
||||
@@ -14,3 +14,12 @@
|
||||
#define COMPANY_NAME "Microsoft Corporation"
|
||||
|
||||
#define COPYRIGHT_NOTE "Copyright (C) 2019 Microsoft Corporation"
|
||||
|
||||
enum class version_architecture
|
||||
{
|
||||
x64,
|
||||
arm
|
||||
};
|
||||
|
||||
version_architecture get_current_architecture();
|
||||
const wchar_t* get_architecture_string(const version_architecture);
|
||||
|
Before Width: | Height: | Size: 983 KiB After Width: | Height: | Size: 319 KiB |
|
Before Width: | Height: | Size: 99 KiB |
BIN
src/modules/fancyzones/FancyZonesSettings1.png
Normal file
|
After Width: | Height: | Size: 71 KiB |
BIN
src/modules/fancyzones/FancyZonesSettings2.png
Normal file
|
After Width: | Height: | Size: 115 KiB |
@@ -1,43 +1,64 @@
|
||||
# Overview
|
||||
Fancy Zones is a window manager that is designed to make it easy to arrange and snap windows into efficient layouts for your workflow and also to restore these layouts quickly. Fancy Zones allows the user to define a set of window locations for a desktop that are drag targets for windows. When the user drags a window into a zone, the windows is resized and repositioned to fill that zone.
|
||||
FancyZones is a window manager that is designed to make it easy to arrange and snap windows into efficient layouts for your workflow and also to restore these layouts quickly. FancyZones allows the user to define a set of window locations for a desktop that are drag targets for windows. When the user drags a window into a zone, the windows is resized and repositioned to fill that zone.
|
||||
|
||||

|
||||

|
||||
|
||||
To get started with Fancy Zones, you need to enable the utility in Power Toys settings and then invoke the Fancy Zones setup UI. There is a button in settings to invoke this UI, or you can press Win+` (note that this shortcut can be changed in the settings dialog) to launch it. When first launched, the UI presents a list of zone layouts that can be quickly adjusted by how many windows are on the monitor. Choosing a layout shows a preview of that layout on the monitor. Pressing the save and close button sets that layout to the monitor.
|
||||
To get started with FancyZones, you need to enable the utility in PowerToys settings and then invoke the FancyZones editor UI.
|
||||
There is a button in settings to invoke the editor, or you can press <code>Win+`</code> (note that this shortcut can be changed in the settings dialog) to launch it.
|
||||
If you have multiple monitors, to edit the zone settings on each monitor, move the mouse to the desired monitor and then press <code>Win+`</code> to launch the editor UI for that monitor, or move the PowerToys settings window to the desired monitor and then launch the editor UI using the `Edit zones` button.
|
||||
|
||||

|
||||
When first launched, the UI presents a list of zone layouts that can be quickly adjusted by how many windows are on the monitor. Choosing a layout shows a preview of that layout on the monitor. Pressing the save and close button sets that layout to the monitor.
|
||||
|
||||
The editor also supports creating and saving custom layouts. This functionality is in the “Custom” tab in the editor UI. There are two ways to create custom zone layouts, window layout and table layout. These can also be thought of as additive and subtractive models. The additive window layout model starts with a blank layout and supports adding zones that can be dragged and resized similar to windows.
|
||||

|
||||
|
||||

|
||||
The editor also supports creating and saving custom layouts. This functionality is in the `Custom` tab in the editor UI.
|
||||
There are two ways to create custom zone layouts, window layout and table layout. These can also be thought of as additive and subtractive models.
|
||||
The additive window layout model starts with a blank layout and supports adding zones that can be dragged and resized similar to windows.
|
||||
|
||||

|
||||
|
||||
The subtractive table layout model starts with a table layout and allows zones to be created by splitting and merging zones and then resizing the gutter between zones.
|
||||
To merge two zone, press and hold the left mouse button and drag the mouse until a second zone is selected, then release the button and a popup menu will show up.
|
||||
|
||||

|
||||

|
||||
|
||||
The backlog for the utility can be found [here](https://github.com/Microsoft/PowerToys/tree/master/doc/planning/FancyZonesBacklog.md) and the source code is [here](https://github.com/Microsoft/PowerToys/tree/master/src/modules/fancyzones).
|
||||
|
||||
# Snapping a window to two zones (EXPERIMENTAL)
|
||||
|
||||
If two zones are adjacent, a window can be snapped to the sum of their area (rounded to the minimum rectangle that contains both). When the mouse cursor in near the common edge of two zones, both zones are activated simultaneously, allowing to drop the window on both.
|
||||
**NOTE:** when snapping a window to two zones, restoring the window when the app is restarted is not supported yet.
|
||||
|
||||

|
||||
|
||||
# Shortcut Keys
|
||||
| Shortcut | Action |
|
||||
| ----------- | ----------- |
|
||||
| Win + ` | Launches editor (this shortcut is editable in the settings dialog) |
|
||||
| Win+Ctrl+\<Number> | Cycles through saved layouts with the corresponding number of zones |
|
||||
| Win+Left/Right Arrow | Move focused window between zones (only if Override snap hotkeys setting is enabled, in that case only the `Win+Left Arrow` and `Win+Right Arrow` are overriden, while the `Win+Up Arrow` and `Win+Down Arrow` keep working as usual) |
|
||||
| Win+Left/Right Arrow | Move focused window between zones (only if `Override Windows Snap hotkeys` setting is turned on, in that case only the `Win+Left Arrow` and `Win+Right Arrow` are overriden, while the `Win+Up Arrow` and `Win+Down Arrow` keep working as usual) |
|
||||
|
||||
FancyZones doesn't override the Windows 10 `Win+Shift+Arrow` to quickly move a window to an adjacent monitor.
|
||||
|
||||
# Settings
|
||||
| Setting | Description |
|
||||
| --------- | ------------- |
|
||||
| Configure the zone editor hotkey | To change the default hotkey, click on the textbox (it's not necessary to select or delete the text) and then press on the keyboard the desired key combination |
|
||||
| Enable zones while dragging with the shift key | Toggles between auto-snap mode with the shift key disabling snapping during a drag and manual snap mode where pressing the shift key during a drag enables snapping |
|
||||
| Override Windows Snap hotkeys (win+arrow) to move between zones | When this option is on and Fancy Zones is running, it overrides two Windows Snap keys: `Win+Left Arrow` and `Win+Right Arrow` |
|
||||
| Flash zones when the active FancyZones layout changes | Briefly flash the zone layout when the layout changes or a new virtual desktop is activated |
|
||||
| Keep windows in their zones when the screen resolution changes | After a screen resolution change, if this setting is enabled, Fancy Zones will resize and reposition windows into the zones they were previously in |
|
||||
| Keep windows in their zones when the active FancyZones layout changes | When this option is on, Fancy Zones will resize and position windows into the new zone layout by maintaining the previous zone number location of each window |
|
||||
| Override Windows Snap hotkeys (Win+Arrow) to move between zones | When this option is on and FancyZones is running, it overrides two Windows Snap keys: `Win+Left Arrow` and `Win+Right Arrow` |
|
||||
| Keep windows in their zones when the screen resolution changes | After a screen resolution change, if this setting is enabled, FancyZones will resize and reposition windows into the zones they were previously in |
|
||||
| Keep windows in their zones when the active FancyZones layout changes | When this option is on, FancyZones will resize and position windows into the new zone layout by maintaining the previous zone number location of each window |
|
||||
| Keep windows in their zones when the active virtual desktop changes | If an application is pinned to all virtual desktops, this setting will keep that window in the same zone on all desktops |
|
||||
| Move newly created windows to the last known zone | Automatically move a newly opened window into the last zone location that application was in |
|
||||
| Follow mouse cursor instead of focus when launching editor in a multi screen environment | When this option is on, the editor hotkey will launch the editor on the monitor where the mouse cursor is, when this option is off, the editor hotkey will launch the editor on monitor where the current active window is |
|
||||
| Zone Highlight Color (Default #0078D7) | the color that a zone becomes when it is the active drop target during a window drag |
|
||||
| Follow mouse cursor instead of focus when launching editor in a multi-monitor environment | When this option is on, the editor hotkey will launch the editor on the monitor where the mouse cursor is, when this option is off, the editor hotkey will launch the editor on monitor where the current active window is |
|
||||
| Show zones on all monitors while dragging a window | By default FancyZones shows only the zones available on the current monitor, this feature may have performance impact when turned on |
|
||||
| Show zones on all monitors while dragging a window | When the zones are activated, the dragged window is made transparent to improve the zones visibility |
|
||||
| Zone highlight color (Default #008CFF) | The color that a zone becomes when it is the active drop target during a window drag |
|
||||
| Zone Inactive color (Default #F5FCFF) | The color that zones become when they are not an active drop during a window drag |
|
||||
| Zone border color (Default #FFFFFF) | The color of the border of active and inactive zones |
|
||||
| Zone opacity (%) (Default 50%) | The percentage of opacity of active and inactive zones |
|
||||
| Exclude applications from snapping to zones | Add the applications name, or part of the name, one per line (e.g., adding `Notepa` will match both `Notepad.exe` and `Notepad++.exe`, to match only `Notepad.exe` add the `.exe` extension) |
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 1.0 MiB |
BIN
src/modules/fancyzones/TwoZonesActivation.png
Normal file
|
After Width: | Height: | Size: 937 KiB |
|
Before Width: | Height: | Size: 246 KiB After Width: | Height: | Size: 1.3 MiB |
@@ -12,6 +12,10 @@
|
||||
<!-- Accent and AppTheme setting -->
|
||||
<ResourceDictionary Source="pack://application:,,,/MahApps.Metro;component/Styles/Themes/Light.Blue.xaml" />
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
|
||||
<SolidColorBrush x:Key="CanvasZoneBackgroundBrush" Color="#BF333333"/>
|
||||
<SolidColorBrush x:Key="GridZoneBackgroundBrush" Color="#FF1a1a1a"/>
|
||||
|
||||
</ResourceDictionary>
|
||||
</Application.Resources>
|
||||
</Application>
|
||||
|
||||
@@ -5,51 +5,113 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:FancyZonesEditor"
|
||||
mc:Ignorable="d"
|
||||
Background="LightGray"
|
||||
Opacity="0.75"
|
||||
Background="Transparent"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<Grid x:Name="Frame">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="24"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="8"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<Thumb x:Name="NWResize" Cursor="SizeNWSE" Background="Black" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted"/>
|
||||
<Thumb x:Name="NEResize" Cursor="SizeNESW" Background="Black" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted"/>
|
||||
<Thumb x:Name="SWResize" Cursor="SizeNESW" Background="Black" Grid.Row="4" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted"/>
|
||||
<Thumb x:Name="SEResize" Cursor="SizeNWSE" Background="Black" Grid.Row="4" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted"/>
|
||||
<Thumb x:Name="NResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="0" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted"/>
|
||||
<Thumb x:Name="SResize" Cursor="SizeNS" Background="Black" Margin="1,0,1,0" Grid.Row="5" Grid.Column="2" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted"/>
|
||||
<Thumb x:Name="WResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="0" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted"/>
|
||||
<Thumb x:Name="EResize" Cursor="SizeWE" Background="Black" Margin="0,1,0,1" Grid.Row="2" Grid.Column="4" Grid.RowSpan="2" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted"/>
|
||||
<DockPanel Grid.Row="1" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3">
|
||||
<Button DockPanel.Dock="Right" Padding="8,0" Click="OnClose">
|
||||
<Image Source="images/ChromeClose.png" Height="24" Width="24" />
|
||||
</Button>
|
||||
<Thumb x:Name="Caption" Cursor="SizeAll" Background="DarkGray" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted"/>
|
||||
</DockPanel>
|
||||
<Rectangle Fill="LightGray" Grid.Row="3" Grid.Column="1" Grid.RowSpan="2" Grid.ColumnSpan="3"/>
|
||||
<Canvas x:Name="Body" />
|
||||
<Label Name="LabelID"
|
||||
|
||||
<UserControl.Resources>
|
||||
<Style x:Key="CanvasZoneThumbStyle" TargetType="{x:Type Thumb}">
|
||||
<Setter Property="Stylus.IsPressAndHoldEnabled" Value="false"/>
|
||||
<Setter Property="Background" Value="Transparent"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Thumb}">
|
||||
<Border x:Name="ThumbBorder" Opacity="0" BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}">
|
||||
<VisualStateManager.VisualStateGroups>
|
||||
<VisualStateGroup x:Name="CommonStates" >
|
||||
<VisualStateGroup.Transitions>
|
||||
<VisualTransition GeneratedDuration="0:0:0.15">
|
||||
<VisualTransition.GeneratedEasingFunction>
|
||||
<ExponentialEase EasingMode="EaseInOut"/>
|
||||
</VisualTransition.GeneratedEasingFunction>
|
||||
</VisualTransition>
|
||||
</VisualStateGroup.Transitions>
|
||||
<VisualState x:Name="Normal" />
|
||||
<VisualState x:Name="MouseOver">
|
||||
<Storyboard>
|
||||
<DoubleAnimation Storyboard.TargetName="ThumbBorder" Duration="0:0:0.15" Storyboard.TargetProperty="Opacity" To="1"/>
|
||||
</Storyboard>
|
||||
</VisualState>
|
||||
</VisualStateGroup>
|
||||
</VisualStateManager.VisualStateGroups>
|
||||
</Border>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
|
||||
<Style x:Key="CloseButtonStyle" TargetType="{x:Type Button}">
|
||||
<Setter Property="BorderThickness" Value="1"/>
|
||||
<Setter Property="HorizontalContentAlignment" Value="Center"/>
|
||||
<Setter Property="VerticalContentAlignment" Value="Center"/>
|
||||
<Setter Property="Padding" Value="1"/>
|
||||
<Setter Property="Template">
|
||||
<Setter.Value>
|
||||
<ControlTemplate TargetType="{x:Type Button}">
|
||||
<Border x:Name="border" BorderBrush="{TemplateBinding BorderBrush}" BorderThickness="{TemplateBinding BorderThickness}" Background="{TemplateBinding Background}" SnapsToDevicePixels="true">
|
||||
<ContentPresenter x:Name="contentPresenter" Focusable="False" HorizontalAlignment="{TemplateBinding HorizontalContentAlignment}" Margin="{TemplateBinding Padding}" RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}" VerticalAlignment="{TemplateBinding VerticalContentAlignment}"/>
|
||||
</Border>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsDefaulted" Value="true">
|
||||
<Setter Property="BorderBrush" TargetName="border" Value="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.6"/>
|
||||
</Trigger>
|
||||
<Trigger Property="IsPressed" Value="true">
|
||||
<Setter Property="Opacity" TargetName="contentPresenter" Value="0.4"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Setter.Value>
|
||||
</Setter>
|
||||
</Style>
|
||||
</UserControl.Resources>
|
||||
|
||||
<Border BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" Background="{StaticResource CanvasZoneBackgroundBrush}" BorderThickness="1">
|
||||
<Grid x:Name="Frame">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="8"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="16"/>
|
||||
<RowDefinition Height="8"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="8"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="16"/>
|
||||
<ColumnDefinition Width="8"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
|
||||
<Label Name="LabelID"
|
||||
Content="ID"
|
||||
Canvas.Left="10"
|
||||
Canvas.Bottom="10"
|
||||
FontSize="80"
|
||||
FontFamily="Segoe UI"
|
||||
Foreground="Black"
|
||||
FontSize="64"
|
||||
FontFamily="Segoe UI Light"
|
||||
Foreground="White"
|
||||
Grid.Column="2"
|
||||
Grid.Row="3"
|
||||
Grid.Row="2"
|
||||
VerticalContentAlignment="Center"
|
||||
HorizontalContentAlignment="Center" />
|
||||
</Grid>
|
||||
|
||||
<Thumb x:Name="Caption" Cursor="SizeAll" Background="Transparent" BorderThickness="3" Padding="4" Grid.Column="0" Grid.ColumnSpan="5" Grid.Row="0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="Caption_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Thumb x:Name="NResize" Cursor="SizeNS" BorderThickness="0,3,0,0" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="NResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SResize" Cursor="SizeNS" BorderThickness="0,0,0,3" Grid.Row="4" Grid.ColumnSpan="5" DragDelta="UniversalDragDelta" DragStarted="SResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="WResize" Cursor="SizeWE" BorderThickness="3,0,0,0" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="WResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="EResize" Cursor="SizeWE" BorderThickness="0,0,3,0" Grid.Column="4" Grid.RowSpan="5" DragDelta="UniversalDragDelta" DragStarted="EResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Thumb x:Name="NWResize" Cursor="SizeNWSE" BorderThickness="3,3,0,0" Grid.Row="0" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="NEResize" Cursor="SizeNESW" BorderThickness="0,3,3,0" Grid.Row="0" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="NEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SWResize" Cursor="SizeNESW" BorderThickness="3,0,0,3" Grid.Row="3" Grid.Column="0" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SWResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
<Thumb x:Name="SEResize" Cursor="SizeNWSE" BorderThickness="0,0,3,3" Grid.Row="3" Grid.Column="3" Grid.RowSpan="2" Grid.ColumnSpan="2" DragDelta="UniversalDragDelta" DragStarted="SEResize_DragStarted" Style="{DynamicResource CanvasZoneThumbStyle}"/>
|
||||
|
||||
<Button Content="" BorderThickness="0" ToolTip="Delete zone" Background="Transparent" Foreground="White" FontSize="16" Padding="4" Click="OnClose" Grid.Row="2" Grid.Column="2" FontFamily="Segoe MDL2 Assets" HorizontalAlignment="Right" VerticalAlignment="Top" Style="{DynamicResource CloseButtonStyle}"/>
|
||||
|
||||
<Canvas x:Name="Body" />
|
||||
|
||||
</Grid>
|
||||
</Border>
|
||||
</UserControl>
|
||||
|
||||
@@ -84,6 +84,9 @@
|
||||
<RunCodeAnalysis>false</RunCodeAnalysis>
|
||||
<CodeAnalysisRuleSet>..\..\..\..\codeAnalysis\Rules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<ApplicationIcon>images\FancyZonesEditor.ico</ApplicationIcon>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.ComponentModel.DataAnnotations" />
|
||||
@@ -256,5 +259,8 @@
|
||||
<ItemGroup>
|
||||
<Resource Include="images\Merge.png" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Resource Include="images\FancyZonesEditor.ico" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -222,6 +222,7 @@ namespace FancyZonesEditor
|
||||
int newChildIndex = AddZone();
|
||||
|
||||
double offset = e.Offset;
|
||||
double space = e.Space;
|
||||
|
||||
if (e.Orientation == Orientation.Vertical)
|
||||
{
|
||||
@@ -294,22 +295,28 @@ namespace FancyZonesEditor
|
||||
model.CellChildMap = newCellChildMap;
|
||||
|
||||
sourceCol = 0;
|
||||
double newTotalExtent = ActualWidth - (space * (cols + 1));
|
||||
for (int col = 0; col < cols; col++)
|
||||
{
|
||||
if (col == foundCol)
|
||||
{
|
||||
RowColInfo[] split = _colInfo[col].Split(offset);
|
||||
RowColInfo[] split = _colInfo[col].Split(offset, space);
|
||||
newColInfo[col] = split[0];
|
||||
newColPercents[col] = split[0].Percent;
|
||||
newColInfo[col++] = split[0];
|
||||
newColPercents[col] = split[1].Percent;
|
||||
col++;
|
||||
|
||||
newColInfo[col] = split[1];
|
||||
sourceCol++;
|
||||
newColPercents[col] = split[1].Percent;
|
||||
}
|
||||
else
|
||||
{
|
||||
newColInfo[col] = _colInfo[sourceCol];
|
||||
newColInfo[col].RecalculatePercent(newTotalExtent);
|
||||
|
||||
newColPercents[col] = model.ColumnPercents[sourceCol];
|
||||
newColInfo[col] = _colInfo[sourceCol++];
|
||||
}
|
||||
|
||||
sourceCol++;
|
||||
}
|
||||
|
||||
_colInfo = newColInfo;
|
||||
@@ -389,22 +396,28 @@ namespace FancyZonesEditor
|
||||
model.CellChildMap = newCellChildMap;
|
||||
|
||||
sourceRow = 0;
|
||||
double newTotalExtent = ActualHeight - (space * (rows + 1));
|
||||
for (int row = 0; row < rows; row++)
|
||||
{
|
||||
if (row == foundRow)
|
||||
{
|
||||
RowColInfo[] split = _rowInfo[row].Split(offset);
|
||||
RowColInfo[] split = _rowInfo[row].Split(offset, space);
|
||||
newRowInfo[row] = split[0];
|
||||
newRowPercents[row] = split[0].Percent;
|
||||
newRowInfo[row++] = split[0];
|
||||
newRowPercents[row] = split[1].Percent;
|
||||
row++;
|
||||
|
||||
newRowInfo[row] = split[1];
|
||||
sourceRow++;
|
||||
newRowPercents[row] = split[1].Percent;
|
||||
}
|
||||
else
|
||||
{
|
||||
newRowInfo[row] = _rowInfo[sourceRow];
|
||||
newRowInfo[row].RecalculatePercent(newTotalExtent);
|
||||
|
||||
newRowPercents[row] = model.RowPercents[sourceRow];
|
||||
newRowInfo[row] = _rowInfo[sourceRow++];
|
||||
}
|
||||
|
||||
sourceRow++;
|
||||
}
|
||||
|
||||
_rowInfo = newRowInfo;
|
||||
|
||||
@@ -9,10 +9,16 @@
|
||||
<Thumb.Template>
|
||||
<ControlTemplate>
|
||||
<StackPanel x:Name="Body" Grid.Column="0" Width="48" Height="48">
|
||||
<Ellipse Height="48" Width="48" Fill="#0078D7" />
|
||||
|
||||
<Ellipse x:Name="BackgroundEllipse" Height="48" Width="48" Fill="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}" />
|
||||
<Rectangle Height="20" Width="2" Fill="White" Margin="5,-48,0,0"/>
|
||||
<Rectangle Height="20" Width="2" Fill="White" Margin="-5,-48,0,0"/>
|
||||
</StackPanel>
|
||||
<ControlTemplate.Triggers>
|
||||
<Trigger Property="IsMouseOver" Value="true">
|
||||
<Setter Property="Opacity" TargetName="BackgroundEllipse" Value="0.6"/>
|
||||
</Trigger>
|
||||
</ControlTemplate.Triggers>
|
||||
</ControlTemplate>
|
||||
</Thumb.Template>
|
||||
</Thumb>
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
|
||||
d:DesignHeight="450"
|
||||
d:DesignWidth="800"
|
||||
Background="LightGray"
|
||||
BorderBrush="DarkGray"
|
||||
Background="{StaticResource GridZoneBackgroundBrush}"
|
||||
BorderBrush="{Binding Source={x:Static SystemParameters.WindowGlassBrush}}"
|
||||
BorderThickness="1"
|
||||
Opacity="0.5"
|
||||
Opacity="0.8"
|
||||
mc:Ignorable="d">
|
||||
<Grid x:Name="Frame">
|
||||
<Canvas x:Name="Body" />
|
||||
@@ -23,9 +23,9 @@
|
||||
HorizontalContentAlignment="Center"
|
||||
VerticalContentAlignment="Center"
|
||||
Content="ID"
|
||||
FontFamily="Segoe UI"
|
||||
FontSize="80"
|
||||
Foreground="Black" />
|
||||
FontFamily="Segoe UI Light"
|
||||
FontSize="64"
|
||||
Foreground="White" />
|
||||
<!--<TextBlock Margin="2" Text="Shift Key switches direction Ctrl Key repeats"/>-->
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace FancyZonesEditor
|
||||
|
||||
private void OnSelectionChanged()
|
||||
{
|
||||
Background = IsSelected ? Brushes.SteelBlue : Brushes.LightGray;
|
||||
Background = IsSelected ? SystemParameters.WindowGlassBrush : App.Current.Resources["GridZoneBackgroundBrush"] as SolidColorBrush;
|
||||
}
|
||||
|
||||
public bool IsSelected
|
||||
@@ -60,7 +60,7 @@ namespace FancyZonesEditor
|
||||
OnSelectionChanged();
|
||||
_splitter = new Rectangle
|
||||
{
|
||||
Fill = Brushes.DarkGray,
|
||||
Fill = SystemParameters.WindowGlassBrush,
|
||||
};
|
||||
Body.Children.Add(_splitter);
|
||||
|
||||
@@ -101,7 +101,16 @@ namespace FancyZonesEditor
|
||||
|
||||
private int SplitterThickness
|
||||
{
|
||||
get { return Math.Max(((App)Application.Current).ZoneSettings.Spacing, 5); }
|
||||
get
|
||||
{
|
||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
||||
if (!settings.ShowSpacing)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
return Math.Max(settings.Spacing, 1);
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateSplitter()
|
||||
@@ -146,7 +155,7 @@ namespace FancyZonesEditor
|
||||
|
||||
protected override void OnMouseEnter(MouseEventArgs e)
|
||||
{
|
||||
_splitter.Fill = Brushes.DarkGray;
|
||||
_splitter.Fill = SystemParameters.WindowGlassBrush; // Active Accent color
|
||||
base.OnMouseEnter(e);
|
||||
}
|
||||
|
||||
@@ -267,8 +276,15 @@ namespace FancyZonesEditor
|
||||
}
|
||||
|
||||
private void DoSplit(Orientation orientation, double offset)
|
||||
{
|
||||
Split?.Invoke(this, new SplitEventArgs(orientation, offset));
|
||||
{
|
||||
int spacing = 0;
|
||||
Settings settings = ((App)Application.Current).ZoneSettings;
|
||||
if (settings.ShowSpacing)
|
||||
{
|
||||
spacing = settings.Spacing;
|
||||
}
|
||||
|
||||
Split?.Invoke(this, new SplitEventArgs(orientation, offset, spacing));
|
||||
}
|
||||
|
||||
private void FullSplit_Click(object sender, RoutedEventArgs e)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -34,13 +34,24 @@ namespace FancyZonesEditor
|
||||
return Extent;
|
||||
}
|
||||
|
||||
public RowColInfo[] Split(double offset)
|
||||
public void RecalculatePercent(double newTotalExtent)
|
||||
{
|
||||
Percent = (int)(Extent * _multiplier / newTotalExtent);
|
||||
}
|
||||
|
||||
public RowColInfo[] Split(double offset, double space)
|
||||
{
|
||||
RowColInfo[] info = new RowColInfo[2];
|
||||
|
||||
int newPercent = (int)(Percent * offset / Extent);
|
||||
info[0] = new RowColInfo(newPercent);
|
||||
info[1] = new RowColInfo(Percent - newPercent);
|
||||
double totalExtent = Extent * _multiplier / Percent;
|
||||
totalExtent -= space;
|
||||
|
||||
int percent0 = (int)(offset * _multiplier / totalExtent);
|
||||
int percent1 = (int)((Extent - space - offset) * _multiplier / totalExtent);
|
||||
|
||||
info[0] = new RowColInfo(percent0);
|
||||
info[1] = new RowColInfo(percent1);
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// Copyright (c) Microsoft Corporation
|
||||
// The Microsoft Corporation licenses this file to you under the MIT license.
|
||||
// See the LICENSE file in the project root for more information.
|
||||
|
||||
@@ -13,15 +13,18 @@ namespace FancyZonesEditor
|
||||
{
|
||||
}
|
||||
|
||||
public SplitEventArgs(Orientation orientation, double offset)
|
||||
public SplitEventArgs(Orientation orientation, double offset, double thickness)
|
||||
{
|
||||
Orientation = orientation;
|
||||
Offset = offset;
|
||||
Space = thickness;
|
||||
}
|
||||
|
||||
public Orientation Orientation { get; }
|
||||
|
||||
public double Offset { get; }
|
||||
|
||||
public double Space { get; }
|
||||
}
|
||||
|
||||
public delegate void SplitEventHandler(object sender, SplitEventArgs args);
|
||||
|
||||
|
After Width: | Height: | Size: 54 KiB |
@@ -9,6 +9,7 @@
|
||||
#include "lib/JsonHelpers.h"
|
||||
#include "lib/ZoneSet.h"
|
||||
#include "trace.h"
|
||||
#include "VirtualDesktopUtils.h"
|
||||
|
||||
#include <functional>
|
||||
#include <common/common.h>
|
||||
@@ -150,6 +151,7 @@ public:
|
||||
void AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept;
|
||||
|
||||
void MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept;
|
||||
void MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept;
|
||||
|
||||
protected:
|
||||
static LRESULT CALLBACK s_WndProc(HWND, UINT, WPARAM, LPARAM) noexcept;
|
||||
@@ -180,6 +182,7 @@ private:
|
||||
};
|
||||
|
||||
bool IsInterestingWindow(HWND window) noexcept;
|
||||
bool IsCursorTypeIndicatingSizeEvent();
|
||||
void UpdateZoneWindows() noexcept;
|
||||
void MoveWindowsOnDisplayChange() noexcept;
|
||||
void UpdateDragState(HWND window, require_write_lock) noexcept;
|
||||
@@ -347,17 +350,28 @@ FancyZones::VirtualDesktopInitialize() noexcept
|
||||
IFACEMETHODIMP_(void)
|
||||
FancyZones::WindowCreated(HWND window) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
if (m_settings->GetSettings()->appLastZone_moveWindows && IsInterestingWindow(window))
|
||||
{
|
||||
for (const auto& [monitor, zoneWindow] : m_zoneWindowMap)
|
||||
{
|
||||
// WindowCreated is also invoked when a virtual desktop switch occurs, we need a way
|
||||
// to figure out when that happens to avoid moving windows that should not be moved.
|
||||
GUID windowDesktopId{};
|
||||
GUID zoneWindowDesktopId{};
|
||||
if (VirtualDesktopUtils::GetWindowDesktopId(window, &windowDesktopId) &&
|
||||
VirtualDesktopUtils::GetZoneWindowDesktopId(zoneWindow.get(), &zoneWindowDesktopId) &&
|
||||
(windowDesktopId != zoneWindowDesktopId))
|
||||
{
|
||||
return;
|
||||
}
|
||||
const auto activeZoneSet = zoneWindow->ActiveZoneSet();
|
||||
if (activeZoneSet)
|
||||
{
|
||||
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(activeZoneSet->Id(), &guidString)))
|
||||
if (SUCCEEDED(StringFromCLSID(activeZoneSet->Id(), &guidString)))
|
||||
{
|
||||
int zoneIndex = fancyZonesData.GetAppLastZoneIndex(window, zoneWindow->UniqueId(), guidString.get());
|
||||
if (zoneIndex != -1)
|
||||
@@ -437,9 +451,6 @@ void FancyZones::ToggleEditor() noexcept
|
||||
HMONITOR monitor{};
|
||||
HWND foregroundWindow{};
|
||||
|
||||
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)
|
||||
@@ -473,24 +484,14 @@ void FancyZones::ToggleEditor() noexcept
|
||||
} })
|
||||
.wait();
|
||||
|
||||
if (use_cursorpos_editor_startupscreen)
|
||||
{
|
||||
DPIAware::GetScreenDPIForPoint(currentCursorPos, dpi_x, dpi_y);
|
||||
}
|
||||
else
|
||||
{
|
||||
DPIAware::GetScreenDPIForWindow(foregroundWindow, dpi_x, dpi_y);
|
||||
}
|
||||
|
||||
auto zoneWindow = iter->second;
|
||||
|
||||
const auto& fancyZonesData = JSONHelpers::FancyZonesDataInstance();
|
||||
fancyZonesData.CustomZoneSetsToJsonFile(ZoneWindowUtils::GetCustomZoneSetsTmpPath());
|
||||
|
||||
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 taskbar_x_offset = mi.rcWork.left - mi.rcMonitor.left;
|
||||
const auto taskbar_y_offset = mi.rcWork.top - mi.rcMonitor.top;
|
||||
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;
|
||||
@@ -692,7 +693,7 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
||||
//const bool flash = m_settings->GetSettings()->zoneSetChange_flashZones && newWorkArea;
|
||||
const bool flash = false;
|
||||
|
||||
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash);
|
||||
auto zoneWindow = MakeZoneWindow(this, m_hinstance, monitor, uniqueId, flash, newWorkArea);
|
||||
if (zoneWindow)
|
||||
{
|
||||
m_zoneWindowMap[monitor] = std::move(zoneWindow);
|
||||
@@ -707,6 +708,12 @@ void FancyZones::AddZoneWindow(HMONITOR monitor, PCWSTR deviceId) noexcept
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int index) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
MoveWindowIntoZoneByIndexSet(window, monitor, { index });
|
||||
}
|
||||
|
||||
void FancyZones::MoveWindowIntoZoneByIndexSet(HWND window, HMONITOR monitor, const std::vector<int>& indexSet) noexcept
|
||||
{
|
||||
std::shared_lock readLock(m_lock);
|
||||
if (window != m_windowMoveSize)
|
||||
@@ -718,7 +725,7 @@ void FancyZones::MoveWindowIntoZoneByIndex(HWND window, HMONITOR monitor, int in
|
||||
if (zoneWindow != m_zoneWindowMap.end())
|
||||
{
|
||||
const auto& zoneWindowPtr = zoneWindow->second;
|
||||
zoneWindowPtr->MoveWindowIntoZoneByIndex(window, index);
|
||||
zoneWindowPtr->MoveWindowIntoZoneByIndexSet(window, indexSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -758,6 +765,33 @@ bool FancyZones::IsInterestingWindow(HWND window) noexcept
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FancyZones::IsCursorTypeIndicatingSizeEvent()
|
||||
{
|
||||
CURSORINFO cursorInfo = { 0 };
|
||||
cursorInfo.cbSize = sizeof(cursorInfo);
|
||||
|
||||
if (::GetCursorInfo(&cursorInfo))
|
||||
{
|
||||
if (::LoadCursor(NULL, IDC_SIZENS) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZEWE) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZENESW) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
if (::LoadCursor(NULL, IDC_SIZENWSE) == cursorInfo.hCursor)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FancyZones::UpdateZoneWindows() noexcept
|
||||
{
|
||||
auto callback = [](HMONITOR monitor, HDC, RECT*, LPARAM data) -> BOOL {
|
||||
@@ -845,10 +879,8 @@ void FancyZones::UpdateDragState(HWND window, require_write_lock) noexcept
|
||||
m_dragEnabled = !(shift | mouse);
|
||||
}
|
||||
|
||||
const bool windowElevated = IsProcessOfWindowElevated(window);
|
||||
static const bool meElevated = is_process_elevated();
|
||||
static bool warning_shown = false;
|
||||
if (windowElevated && !meElevated)
|
||||
if (!is_process_elevated() && IsProcessOfWindowElevated(window))
|
||||
{
|
||||
m_dragEnabled = false;
|
||||
if (!warning_shown && !is_cant_drag_elevated_warning_disabled())
|
||||
@@ -933,23 +965,10 @@ bool FancyZones::OnSnapHotkey(DWORD vkCode) noexcept
|
||||
|
||||
void FancyZones::MoveSizeStartInternal(HWND window, HMONITOR monitor, POINT const& ptScreen, require_write_lock writeLock) noexcept
|
||||
{
|
||||
// Only enter move/size if the cursor is inside the window rect by a certain padding.
|
||||
// This prevents resize from triggering zones.
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
|
||||
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) == FALSE)
|
||||
if (IsCursorTypeIndicatingSizeEvent())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_inMoveSize = true;
|
||||
|
||||
auto iter = m_zoneWindowMap.find(monitor);
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
<ClInclude Include="Settings.h" />
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="util.h" />
|
||||
<ClInclude Include="VirtualDesktopUtils.h" />
|
||||
<ClInclude Include="Zone.h" />
|
||||
<ClInclude Include="ZoneSet.h" />
|
||||
<ClInclude Include="ZoneWindow.h" />
|
||||
@@ -117,6 +118,7 @@
|
||||
<ClCompile Include="Settings.cpp" />
|
||||
<ClCompile Include="trace.cpp" />
|
||||
<ClCompile Include="util.cpp" />
|
||||
<ClCompile Include="VirtualDesktopUtils.cpp" />
|
||||
<ClCompile Include="Zone.cpp" />
|
||||
<ClCompile Include="ZoneSet.cpp" />
|
||||
<ClCompile Include="ZoneWindow.cpp" />
|
||||
|
||||
@@ -48,6 +48,9 @@
|
||||
<ClInclude Include="JsonHelpers.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="VirtualDesktopUtils.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="pch.cpp">
|
||||
@@ -77,6 +80,9 @@
|
||||
<ClCompile Include="util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VirtualDesktopUtils.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="fancyzones.rc">
|
||||
|
||||
@@ -604,9 +604,6 @@ namespace JSONHelpers
|
||||
|
||||
if (!std::filesystem::exists(jsonFilePath))
|
||||
{
|
||||
TmpMigrateAppliedZoneSetsFromRegistry();
|
||||
|
||||
// Custom zone sets have to be migrated after applied zone sets!
|
||||
MigrateCustomZoneSetsFromRegistry();
|
||||
|
||||
SaveFancyZonesData();
|
||||
@@ -639,56 +636,6 @@ namespace JSONHelpers
|
||||
json::to_file(jsonFilePath, root);
|
||||
}
|
||||
|
||||
void FancyZonesData::TmpMigrateAppliedZoneSetsFromRegistry()
|
||||
{
|
||||
std::wregex ex(L"^[0-9]{3,4}_[0-9]{3,4}$");
|
||||
|
||||
std::scoped_lock lock{ dataLock };
|
||||
wchar_t key[256];
|
||||
StringCchPrintf(key, ARRAYSIZE(key), L"%s", RegistryHelpers::REG_SETTINGS);
|
||||
HKEY hkey;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, key, 0, KEY_ALL_ACCESS, &hkey) == ERROR_SUCCESS)
|
||||
{
|
||||
wchar_t resolutionKey[256]{};
|
||||
DWORD resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||
DWORD i = 0;
|
||||
while (RegEnumKeyW(hkey, i++, resolutionKey, resolutionKeyLength) == ERROR_SUCCESS)
|
||||
{
|
||||
std::wstring resolution{ resolutionKey };
|
||||
wchar_t appliedZoneSetskey[256];
|
||||
StringCchPrintf(appliedZoneSetskey, ARRAYSIZE(appliedZoneSetskey), L"%s\\%s", RegistryHelpers::REG_SETTINGS, resolutionKey);
|
||||
HKEY appliedZoneSetsHkey;
|
||||
if (std::regex_match(resolution, ex) && RegOpenKeyExW(HKEY_CURRENT_USER, appliedZoneSetskey, 0, KEY_ALL_ACCESS, &appliedZoneSetsHkey) == ERROR_SUCCESS)
|
||||
{
|
||||
ZoneSetPersistedDataOLD data;
|
||||
DWORD dataSize = sizeof(data);
|
||||
wchar_t value[256]{};
|
||||
DWORD valueLength = ARRAYSIZE(value);
|
||||
DWORD i = 0;
|
||||
|
||||
while (RegEnumValueW(appliedZoneSetsHkey, i++, value, &valueLength, nullptr, nullptr, reinterpret_cast<BYTE*>(&data), &dataSize) == ERROR_SUCCESS)
|
||||
{
|
||||
ZoneSetData appliedZoneSetData;
|
||||
appliedZoneSetData.type = TypeFromLayoutId(data.LayoutId);
|
||||
if (appliedZoneSetData.type != ZoneSetLayoutType::Custom)
|
||||
{
|
||||
appliedZoneSetData.uuid = std::wstring{ value };
|
||||
}
|
||||
else
|
||||
{
|
||||
// uuid is changed later to actual uuid when migrating custom zone sets
|
||||
appliedZoneSetData.uuid = std::to_wstring(data.LayoutId);
|
||||
}
|
||||
appliedZoneSetsMap[value] = appliedZoneSetData;
|
||||
dataSize = sizeof(data);
|
||||
valueLength = ARRAYSIZE(value);
|
||||
}
|
||||
}
|
||||
resolutionKeyLength = ARRAYSIZE(resolutionKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FancyZonesData::MigrateCustomZoneSetsFromRegistry()
|
||||
{
|
||||
std::scoped_lock lock{ dataLock };
|
||||
@@ -709,29 +656,19 @@ namespace JSONHelpers
|
||||
zoneSetData.type = static_cast<CustomLayoutType>(data[2]);
|
||||
// int version = data[0] * 256 + data[1]; - Not used anymore
|
||||
|
||||
std::wstring uuid = std::to_wstring(data[3] * 256 + data[4]);
|
||||
auto it = std::find_if(appliedZoneSetsMap.cbegin(), appliedZoneSetsMap.cend(), [&uuid](std::pair<std::wstring, ZoneSetData> zoneSetMap) {
|
||||
return zoneSetMap.second.uuid.compare(uuid) == 0;
|
||||
});
|
||||
GUID guid;
|
||||
auto result = CoCreateGuid(&guid);
|
||||
if (result != S_OK)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (!SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it != appliedZoneSetsMap.cend())
|
||||
{
|
||||
uuid = it->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
GUID guid;
|
||||
auto result = CoCreateGuid(&guid);
|
||||
if (result != S_OK)
|
||||
{
|
||||
return;
|
||||
}
|
||||
wil::unique_cotaskmem_string guidString;
|
||||
if (SUCCEEDED_LOG(StringFromCLSID(guid, &guidString)))
|
||||
{
|
||||
uuid = guidString.get();
|
||||
}
|
||||
}
|
||||
std::wstring uuid = guidString.get();
|
||||
|
||||
switch (zoneSetData.type)
|
||||
{
|
||||
|
||||
@@ -207,12 +207,16 @@ namespace JSONHelpers
|
||||
#if defined(UNIT_TESTS)
|
||||
inline void clear_data()
|
||||
{
|
||||
appliedZoneSetsMap.clear();
|
||||
appZoneHistoryMap.clear();
|
||||
deviceInfoMap.clear();
|
||||
customZoneSetsMap.clear();
|
||||
activeDeviceId.clear();
|
||||
}
|
||||
|
||||
inline void SetDeviceInfo(const std::wstring& deviceId, DeviceInfoData data)
|
||||
{
|
||||
deviceInfoMap[deviceId] = data;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline void SetActiveDeviceId(const std::wstring& deviceId)
|
||||
@@ -254,10 +258,8 @@ namespace JSONHelpers
|
||||
void SaveFancyZonesData() const;
|
||||
|
||||
private:
|
||||
void TmpMigrateAppliedZoneSetsFromRegistry();
|
||||
void MigrateCustomZoneSetsFromRegistry();
|
||||
|
||||
std::unordered_map<std::wstring, ZoneSetData> appliedZoneSetsMap{};
|
||||
std::unordered_map<std::wstring, AppZoneHistoryData> appZoneHistoryMap{};
|
||||
std::unordered_map<std::wstring, DeviceInfoData> deviceInfoMap{};
|
||||
std::unordered_map<std::wstring, CustomZoneSetData> customZoneSetsMap{};
|
||||
|
||||
49
src/modules/fancyzones/lib/VirtualDesktopUtils.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
#include "pch.h"
|
||||
|
||||
#include "VirtualDesktopUtils.h"
|
||||
|
||||
namespace VirtualDesktopUtils
|
||||
{
|
||||
const CLSID CLSID_ImmersiveShell = { 0xC2F03A33, 0x21F5, 0x47FA, 0xB4, 0xBB, 0x15, 0x63, 0x62, 0xA2, 0xF2, 0x39 };
|
||||
const wchar_t GUID_EmptyGUID[] = L"{00000000-0000-0000-0000-000000000000}";
|
||||
|
||||
IServiceProvider* GetServiceProvider()
|
||||
{
|
||||
IServiceProvider* provider{ nullptr };
|
||||
if (FAILED(CoCreateInstance(CLSID_ImmersiveShell, nullptr, CLSCTX_LOCAL_SERVER, __uuidof(provider), (PVOID*)&provider)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return provider;
|
||||
}
|
||||
|
||||
IVirtualDesktopManager* GetVirtualDesktopManager()
|
||||
{
|
||||
IVirtualDesktopManager* manager{ nullptr };
|
||||
IServiceProvider* serviceProvider = GetServiceProvider();
|
||||
if (serviceProvider == nullptr || FAILED(serviceProvider->QueryService(__uuidof(manager), &manager)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
return manager;
|
||||
}
|
||||
|
||||
bool GetWindowDesktopId(HWND topLevelWindow, GUID* desktopId)
|
||||
{
|
||||
static IVirtualDesktopManager* virtualDesktopManager = GetVirtualDesktopManager();
|
||||
return (virtualDesktopManager != nullptr) &&
|
||||
SUCCEEDED(virtualDesktopManager->GetWindowDesktopId(topLevelWindow, desktopId));
|
||||
}
|
||||
|
||||
bool GetZoneWindowDesktopId(IZoneWindow* zoneWindow, GUID* desktopId)
|
||||
{
|
||||
// Format: <device-id>_<resolution>_<virtual-desktop-id>
|
||||
std::wstring uniqueId = zoneWindow->UniqueId();
|
||||
std::wstring virtualDesktopId = uniqueId.substr(uniqueId.rfind('_') + 1);
|
||||
if (virtualDesktopId == GUID_EmptyGUID)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return SUCCEEDED(CLSIDFromString(virtualDesktopId.c_str(), desktopId));
|
||||
}
|
||||
}
|
||||
9
src/modules/fancyzones/lib/VirtualDesktopUtils.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "ZoneWindow.h"
|
||||
|
||||
namespace VirtualDesktopUtils
|
||||
{
|
||||
bool GetWindowDesktopId(HWND topLevelWindow, GUID* desktopId);
|
||||
bool GetZoneWindowDesktopId(IZoneWindow* zoneWindow, GUID* desktopId);
|
||||
}
|
||||
@@ -1,9 +1,15 @@
|
||||
#include "pch.h"
|
||||
|
||||
|
||||
#include <Shellscalingapi.h>
|
||||
|
||||
#include <common/dpi_aware.h>
|
||||
#include <common/monitors.h>
|
||||
#include "Zone.h"
|
||||
#include "Settings.h"
|
||||
#include "util.h"
|
||||
|
||||
#include "common/monitors.h"
|
||||
|
||||
struct Zone : winrt::implements<Zone, IZone>
|
||||
{
|
||||
@@ -20,6 +26,7 @@ public:
|
||||
IFACEMETHODIMP_(void) RemoveWindowFromZone(HWND window, bool restoreSize) noexcept;
|
||||
IFACEMETHODIMP_(void) SetId(size_t id) noexcept { m_id = id; }
|
||||
IFACEMETHODIMP_(size_t) Id() noexcept { return m_id; }
|
||||
IFACEMETHODIMP_(RECT) ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept;
|
||||
|
||||
private:
|
||||
void SizeWindowToZone(HWND window, HWND zoneWindow) noexcept;
|
||||
@@ -61,14 +68,52 @@ IFACEMETHODIMP_(void) Zone::RemoveWindowFromZone(HWND window, bool restoreSize)
|
||||
|
||||
void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
// Skip invisible windows
|
||||
if (!IsWindowVisible(window))
|
||||
SizeWindowToRect(window, ComputeActualZoneRect(window, zoneWindow));
|
||||
}
|
||||
|
||||
static BOOL CALLBACK saveDisplayToVector(HMONITOR monitor, HDC hdc, LPRECT rect, LPARAM data)
|
||||
{
|
||||
reinterpret_cast<std::vector<HMONITOR>*>(data)->emplace_back(monitor);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool allMonitorsHaveSameDpiScaling()
|
||||
{
|
||||
std::vector<HMONITOR> monitors;
|
||||
EnumDisplayMonitors(NULL, NULL, saveDisplayToVector, reinterpret_cast<LPARAM>(&monitors));
|
||||
|
||||
if (monitors.size() < 2)
|
||||
{
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
UINT firstMonitorDpiX;
|
||||
UINT firstMonitorDpiY;
|
||||
|
||||
if (S_OK != GetDpiForMonitor(monitors[0], MDT_EFFECTIVE_DPI, &firstMonitorDpiX, &firstMonitorDpiY))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int i = 1; i < monitors.size(); i++)
|
||||
{
|
||||
UINT iteratedMonitorDpiX;
|
||||
UINT iteratedMonitorDpiY;
|
||||
|
||||
if (S_OK != GetDpiForMonitor(monitors[i], MDT_EFFECTIVE_DPI, &iteratedMonitorDpiX, &iteratedMonitorDpiY) ||
|
||||
iteratedMonitorDpiX != firstMonitorDpiX)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
RECT Zone::ComputeActualZoneRect(HWND window, HWND zoneWindow) noexcept
|
||||
{
|
||||
// Take care of 1px border
|
||||
RECT zoneRect = m_zoneRect;
|
||||
RECT newWindowRect = m_zoneRect;
|
||||
|
||||
RECT windowRect{};
|
||||
::GetWindowRect(window, &windowRect);
|
||||
@@ -77,47 +122,43 @@ void Zone::SizeWindowToZone(HWND window, HWND zoneWindow) noexcept
|
||||
|
||||
const auto level = DPIAware::GetAwarenessLevel(GetWindowDpiAwarenessContext(window));
|
||||
const bool accountForUnawareness = level < DPIAware::PER_MONITOR_AWARE;
|
||||
|
||||
if (SUCCEEDED(DwmGetWindowAttribute(window, DWMWA_EXTENDED_FRAME_BOUNDS, &frameRect, sizeof(frameRect))))
|
||||
{
|
||||
const auto left_margin = frameRect.left - windowRect.left;
|
||||
const auto right_margin = frameRect.right - windowRect.right;
|
||||
const auto bottom_margin = frameRect.bottom - windowRect.bottom;
|
||||
zoneRect.left -= left_margin;
|
||||
zoneRect.right -= right_margin;
|
||||
zoneRect.bottom -= bottom_margin;
|
||||
LONG leftMargin = frameRect.left - windowRect.left;
|
||||
LONG rightMargin = frameRect.right - windowRect.right;
|
||||
LONG bottomMargin = frameRect.bottom - windowRect.bottom;
|
||||
newWindowRect.left -= leftMargin;
|
||||
newWindowRect.right -= rightMargin;
|
||||
newWindowRect.bottom -= bottomMargin;
|
||||
}
|
||||
|
||||
// Map to screen coords
|
||||
MapWindowRect(zoneWindow, nullptr, &zoneRect);
|
||||
MapWindowRect(zoneWindow, nullptr, &newWindowRect);
|
||||
|
||||
MONITORINFO mi{sizeof(mi)};
|
||||
MONITORINFO mi{ sizeof(mi) };
|
||||
if (GetMonitorInfoW(MonitorFromWindow(zoneWindow, MONITOR_DEFAULTTONEAREST), &mi))
|
||||
{
|
||||
const auto taskbar_left_size = std::abs(mi.rcMonitor.left - mi.rcWork.left);
|
||||
const auto taskbar_top_size = std::abs(mi.rcMonitor.top - mi.rcWork.top);
|
||||
OffsetRect(&zoneRect, -taskbar_left_size, -taskbar_top_size);
|
||||
if (accountForUnawareness)
|
||||
OffsetRect(&newWindowRect, -taskbar_left_size, -taskbar_top_size);
|
||||
|
||||
if (accountForUnawareness && !allMonitorsHaveSameDpiScaling())
|
||||
{
|
||||
zoneRect.left = max(mi.rcMonitor.left, zoneRect.left);
|
||||
zoneRect.right = min(mi.rcMonitor.right - taskbar_left_size, zoneRect.right);
|
||||
zoneRect.top = max(mi.rcMonitor.top, zoneRect.top);
|
||||
zoneRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, zoneRect.bottom);
|
||||
newWindowRect.left = max(mi.rcMonitor.left, newWindowRect.left);
|
||||
newWindowRect.right = min(mi.rcMonitor.right - taskbar_left_size, newWindowRect.right);
|
||||
newWindowRect.top = max(mi.rcMonitor.top, newWindowRect.top);
|
||||
newWindowRect.bottom = min(mi.rcMonitor.bottom - taskbar_top_size, newWindowRect.bottom);
|
||||
}
|
||||
}
|
||||
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
placement.rcNormalPosition = zoneRect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
|
||||
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
|
||||
if ((::GetWindowLong(window, GWL_STYLE) & WS_SIZEBOX) == 0)
|
||||
{
|
||||
placement.showCmd = SW_RESTORE | SW_SHOWNA;
|
||||
newWindowRect.right = newWindowRect.left + (windowRect.right - windowRect.left);
|
||||
newWindowRect.bottom = newWindowRect.top + (windowRect.bottom - windowRect.top);
|
||||
}
|
||||
::SetWindowPlacement(window, &placement);
|
||||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
::SetWindowPlacement(window, &placement);
|
||||
|
||||
return newWindowRect;
|
||||
}
|
||||
|
||||
void Zone::StampZone(HWND window, bool stamp) noexcept
|
||||
|
||||
@@ -42,9 +42,20 @@ interface __declspec(uuid("{8228E934-B6EF-402A-9892-15A1441BF8B0}")) IZone : pub
|
||||
*/
|
||||
IFACEMETHOD_(void, SetId)(size_t id) = 0;
|
||||
/**
|
||||
* @retirns Zone identifier.
|
||||
* @returns Zone identifier.
|
||||
*/
|
||||
IFACEMETHOD_(size_t, Id)() = 0;
|
||||
|
||||
/**
|
||||
* Compute the coordinates of the rectangle to which a window should be resized.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @returns a RECT structure, describing global coordinates to which a window should be resized
|
||||
*/
|
||||
IFACEMETHOD_(RECT, ComputeActualZoneRect)(HWND window, HWND zoneWindow) = 0;
|
||||
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZone> MakeZone(const RECT& zoneRect) noexcept;
|
||||
|
||||
@@ -122,14 +122,16 @@ public:
|
||||
IFACEMETHODIMP_(JSONHelpers::ZoneSetLayoutType)
|
||||
LayoutType() noexcept { return m_config.LayoutType; }
|
||||
IFACEMETHODIMP AddZone(winrt::com_ptr<IZone> zone) noexcept;
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||
ZoneFromPoint(POINT pt) noexcept;
|
||||
IFACEMETHODIMP_(std::vector<int>)
|
||||
ZonesFromPoint(POINT pt) noexcept;
|
||||
IFACEMETHODIMP_(int)
|
||||
GetZoneIndexFromWindow(HWND window) noexcept;
|
||||
IFACEMETHODIMP_(std::vector<winrt::com_ptr<IZone>>)
|
||||
GetZones() noexcept { return m_zones; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index) noexcept;
|
||||
MoveWindowIntoZoneByIndex(HWND window, HWND zoneWindow, int index, bool stampZone) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, HWND zoneWindow, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
@@ -162,41 +164,79 @@ IFACEMETHODIMP ZoneSet::AddZone(winrt::com_ptr<IZone> zone) noexcept
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(winrt::com_ptr<IZone>)
|
||||
ZoneSet::ZoneFromPoint(POINT pt) noexcept
|
||||
IFACEMETHODIMP_(std::vector<int>)
|
||||
ZoneSet::ZonesFromPoint(POINT pt) noexcept
|
||||
{
|
||||
winrt::com_ptr<IZone> smallestKnownZone = nullptr;
|
||||
// To reduce redundant calculations, we will store the last known zones area.
|
||||
int smallestKnownZoneArea = INT32_MAX;
|
||||
for (auto iter = m_zones.rbegin(); iter != m_zones.rend(); iter++)
|
||||
const int SENSITIVITY_RADIUS = 20;
|
||||
std::vector<int> capturedZones;
|
||||
std::vector<int> strictlyCapturedZones;
|
||||
for (size_t i = 0; i < m_zones.size(); i++)
|
||||
{
|
||||
if (winrt::com_ptr<IZone> zone = iter->try_as<IZone>())
|
||||
auto zone = m_zones[i];
|
||||
RECT newZoneRect = zone->GetZoneRect();
|
||||
if (newZoneRect.left < newZoneRect.right && newZoneRect.top < newZoneRect.bottom) // proper zone
|
||||
{
|
||||
RECT* newZoneRect = &zone->GetZoneRect();
|
||||
if (PtInRect(newZoneRect, pt))
|
||||
if (newZoneRect.left - SENSITIVITY_RADIUS <= pt.x && pt.x <= newZoneRect.right + SENSITIVITY_RADIUS &&
|
||||
newZoneRect.top - SENSITIVITY_RADIUS <= pt.y && pt.y <= newZoneRect.bottom + SENSITIVITY_RADIUS)
|
||||
{
|
||||
if (smallestKnownZone == nullptr)
|
||||
{
|
||||
smallestKnownZone = zone;
|
||||
|
||||
RECT* r = &smallestKnownZone->GetZoneRect();
|
||||
smallestKnownZoneArea = (r->right - r->left) * (r->bottom - r->top);
|
||||
}
|
||||
else
|
||||
{
|
||||
int newZoneArea = (newZoneRect->right - newZoneRect->left) * (newZoneRect->bottom - newZoneRect->top);
|
||||
|
||||
if (newZoneArea < smallestKnownZoneArea)
|
||||
{
|
||||
smallestKnownZone = zone;
|
||||
smallestKnownZoneArea = newZoneArea;
|
||||
}
|
||||
}
|
||||
capturedZones.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
|
||||
if (newZoneRect.left <= pt.x && pt.x < newZoneRect.right &&
|
||||
newZoneRect.top <= pt.y && pt.y < newZoneRect.bottom)
|
||||
{
|
||||
strictlyCapturedZones.emplace_back(static_cast<int>(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return smallestKnownZone;
|
||||
// If only one zone is captured, but it's not strictly captured
|
||||
// don't consider it as captured
|
||||
if (capturedZones.size() == 1 && strictlyCapturedZones.size() == 0)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
|
||||
// If captured zones do not overlap, return all of them
|
||||
// Otherwise, return the smallest one
|
||||
|
||||
bool overlap = false;
|
||||
for (size_t i = 0; i < capturedZones.size(); ++i)
|
||||
{
|
||||
for (size_t j = i + 1; j < capturedZones.size(); ++j)
|
||||
{
|
||||
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
|
||||
auto rectJ = m_zones[capturedZones[j]]->GetZoneRect();
|
||||
if (max(rectI.top, rectJ.top) < min(rectI.bottom, rectJ.bottom) &&
|
||||
max(rectI.left, rectJ.left) < min(rectI.right, rectJ.right))
|
||||
{
|
||||
overlap = true;
|
||||
i = capturedZones.size() - 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (overlap)
|
||||
{
|
||||
size_t smallestIdx = 0;
|
||||
for (size_t i = 1; i < capturedZones.size(); ++i)
|
||||
{
|
||||
auto rectS = m_zones[capturedZones[smallestIdx]]->GetZoneRect();
|
||||
auto rectI = m_zones[capturedZones[i]]->GetZoneRect();
|
||||
int smallestSize = (rectS.bottom - rectS.top) * (rectS.right - rectS.left);
|
||||
int iSize = (rectI.bottom - rectI.top) * (rectI.right - rectI.left);
|
||||
|
||||
if (iSize <= smallestSize)
|
||||
{
|
||||
smallestIdx = i;
|
||||
}
|
||||
}
|
||||
|
||||
capturedZones = { capturedZones[smallestIdx] };
|
||||
}
|
||||
|
||||
return capturedZones;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(int)
|
||||
@@ -217,7 +257,7 @@ ZoneSet::GetZoneIndexFromWindow(HWND window) noexcept
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noexcept
|
||||
ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index, bool stampZone) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
@@ -226,7 +266,7 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
|
||||
|
||||
if (index >= int(m_zones.size()))
|
||||
{
|
||||
index = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
while (auto zoneDrop = ZoneFromWindow(window))
|
||||
@@ -236,7 +276,55 @@ ZoneSet::MoveWindowIntoZoneByIndex(HWND window, HWND windowZone, int index) noex
|
||||
|
||||
if (auto zone = m_zones.at(index))
|
||||
{
|
||||
zone->AddWindowToZone(window, windowZone, false);
|
||||
zone->AddWindowToZone(window, windowZone, stampZone);
|
||||
}
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneSet::MoveWindowIntoZoneByIndexSet(HWND window, HWND windowZone, const std::vector<int>& indexSet, bool stampZone) noexcept
|
||||
{
|
||||
if (m_zones.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while (auto zoneDrop = ZoneFromWindow(window))
|
||||
{
|
||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||
}
|
||||
|
||||
if (indexSet.size() == 1)
|
||||
{
|
||||
MoveWindowIntoZoneByIndex(window, windowZone, indexSet[0], stampZone);
|
||||
return;
|
||||
}
|
||||
|
||||
RECT size;
|
||||
bool sizeEmpty = true;
|
||||
|
||||
for (int index : indexSet)
|
||||
{
|
||||
if (index < static_cast<int>(m_zones.size()))
|
||||
{
|
||||
RECT newSize = m_zones.at(index)->ComputeActualZoneRect(window, windowZone);
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
size.left = min(size.left, newSize.left);
|
||||
size.top = min(size.top, newSize.top);
|
||||
size.right = max(size.right, newSize.right);
|
||||
size.bottom = max(size.bottom, newSize.bottom);
|
||||
}
|
||||
else
|
||||
{
|
||||
size = newSize;
|
||||
sizeEmpty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!sizeEmpty)
|
||||
{
|
||||
SizeWindowToRect(window, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -306,10 +394,8 @@ ZoneSet::MoveWindowIntoZoneByPoint(HWND window, HWND zoneWindow, POINT ptClient)
|
||||
zoneDrop->RemoveWindowFromZone(window, !IsZoomed(window));
|
||||
}
|
||||
|
||||
if (auto zone = ZoneFromPoint(ptClient))
|
||||
{
|
||||
zone->AddWindowToZone(window, zoneWindow, true);
|
||||
}
|
||||
auto zones = ZonesFromPoint(ptClient);
|
||||
MoveWindowIntoZoneByIndexSet(window, zoneWindow, zones, true);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(bool)
|
||||
|
||||
@@ -24,12 +24,12 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
*/
|
||||
IFACEMETHOD(AddZone)(winrt::com_ptr<IZone> zone) = 0;
|
||||
/**
|
||||
* Get zone from cursor coordinates.
|
||||
* Get zones from cursor coordinates.
|
||||
*
|
||||
* @param pt Cursor coordinates.
|
||||
* @returns Zone object (defining coordinates of the zone).
|
||||
* @returns Vector of indices, corresponding to the current set of zones - the zones considered active.
|
||||
*/
|
||||
IFACEMETHOD_(winrt::com_ptr<IZone>, ZoneFromPoint)(POINT pt) = 0;
|
||||
IFACEMETHOD_(std::vector<int>, ZonesFromPoint)(POINT pt) = 0;
|
||||
/**
|
||||
* Get index of the zone inside zone layout by window assigned to it.
|
||||
*
|
||||
@@ -48,8 +48,20 @@ interface __declspec(uuid("{E4839EB7-669D-49CF-84A9-71A2DFD851A3}")) IZoneSet :
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param index Zone index within zone layout.
|
||||
* @param stampZone Whether the window being added to the zone should be stamped.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index) = 0;
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, HWND zoneWindow, int index, bool stampZone) = 0;
|
||||
/**
|
||||
* Assign window to the zones based on the set of zone indices inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param zoneWindow The m_window of a ZoneWindow, it's a hidden window representing the
|
||||
* current monitor desktop work area.
|
||||
* @param indexSet The set of zone indices within zone layout.
|
||||
* @param stampZone Whether the window being added to the zone should be stamped,
|
||||
in case a single window is to be added.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, HWND zoneWindow, const std::vector<int>& indexSet, bool stampZone) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
|
||||
*
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace ZoneWindowDrawUtils
|
||||
Gdiplus::Color fillColor(colorSetting.fillAlpha, GetRValue(colorSetting.fill), GetGValue(colorSetting.fill), GetBValue(colorSetting.fill));
|
||||
Gdiplus::Color borderColor(colorSetting.borderAlpha, GetRValue(colorSetting.border), GetGValue(colorSetting.border), GetBValue(colorSetting.border));
|
||||
|
||||
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left, zoneRect.bottom - zoneRect.top);
|
||||
Gdiplus::Rect rectangle(zoneRect.left, zoneRect.top, zoneRect.right - zoneRect.left - 1, zoneRect.bottom - zoneRect.top - 1);
|
||||
|
||||
Gdiplus::Pen pen(borderColor, static_cast<Gdiplus::REAL>(colorSetting.thickness));
|
||||
g.FillRectangle(new Gdiplus::SolidBrush(fillColor), rectangle);
|
||||
@@ -148,7 +148,7 @@ namespace ZoneWindowDrawUtils
|
||||
COLORREF highlightColor,
|
||||
int zoneOpacity,
|
||||
const std::vector<winrt::com_ptr<IZone>>& zones,
|
||||
const winrt::com_ptr<IZone>& highlightZone,
|
||||
const std::vector<int>& highlightZones,
|
||||
bool flashMode,
|
||||
bool drawHints) noexcept
|
||||
{
|
||||
@@ -158,15 +158,22 @@ namespace ZoneWindowDrawUtils
|
||||
ColorSetting colorHighlight{ OpacitySettingToAlpha(zoneOpacity), 0, 255, 0, -2 };
|
||||
ColorSetting const colorFlash{ OpacitySettingToAlpha(zoneOpacity), RGB(81, 92, 107), 200, RGB(104, 118, 138), -2 };
|
||||
|
||||
std::vector<bool> isHighlighted(zones.size(), false);
|
||||
for (int x : highlightZones)
|
||||
{
|
||||
isHighlighted[x] = true;
|
||||
}
|
||||
|
||||
for (auto iter = zones.begin(); iter != zones.end(); iter++)
|
||||
{
|
||||
int zoneId = static_cast<int>(iter - zones.begin());
|
||||
winrt::com_ptr<IZone> zone = iter->try_as<IZone>();
|
||||
if (!zone)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (zone != highlightZone)
|
||||
if (!isHighlighted[zoneId])
|
||||
{
|
||||
if (flashMode)
|
||||
{
|
||||
@@ -182,13 +189,12 @@ namespace ZoneWindowDrawUtils
|
||||
DrawZone(hdc, colorViewer, zone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (highlightZone)
|
||||
{
|
||||
colorHighlight.fill = highlightColor;
|
||||
colorHighlight.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorHighlight, highlightZone, zones, flashMode);
|
||||
else
|
||||
{
|
||||
colorHighlight.fill = highlightColor;
|
||||
colorHighlight.border = zoneBorderColor;
|
||||
DrawZone(hdc, colorHighlight, zone, zones, flashMode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -199,7 +205,7 @@ public:
|
||||
ZoneWindow(HINSTANCE hinstance);
|
||||
~ZoneWindow();
|
||||
|
||||
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones);
|
||||
bool Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea);
|
||||
|
||||
IFACEMETHODIMP MoveSizeEnter(HWND window, bool dragEnabled) noexcept;
|
||||
IFACEMETHODIMP MoveSizeUpdate(POINT const& ptScreen, bool dragEnabled) noexcept;
|
||||
@@ -210,6 +216,8 @@ public:
|
||||
IsDragEnabled() noexcept { return m_dragEnabled; }
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndex(HWND window, int index) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept;
|
||||
IFACEMETHODIMP_(bool)
|
||||
MoveWindowIntoZoneByDirection(HWND window, DWORD vkCode, bool cycle) noexcept;
|
||||
IFACEMETHODIMP_(void)
|
||||
@@ -232,13 +240,13 @@ protected:
|
||||
|
||||
private:
|
||||
void LoadSettings() noexcept;
|
||||
void InitializeZoneSets(MONITORINFO const& mi) noexcept;
|
||||
void InitializeZoneSets(bool newWorkArea) noexcept;
|
||||
void CalculateZoneSet() noexcept;
|
||||
void UpdateActiveZoneSet(_In_opt_ IZoneSet* zoneSet) noexcept;
|
||||
LRESULT WndProc(UINT message, WPARAM wparam, LPARAM lparam) noexcept;
|
||||
void OnPaint(wil::unique_hdc& hdc) noexcept;
|
||||
void OnKeyUp(WPARAM wparam) noexcept;
|
||||
winrt::com_ptr<IZone> ZoneFromPoint(POINT pt) noexcept;
|
||||
std::vector<int> ZonesFromPoint(POINT pt) noexcept;
|
||||
void CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept;
|
||||
void FlashZones() noexcept;
|
||||
|
||||
@@ -253,7 +261,7 @@ private:
|
||||
bool m_dragEnabled{};
|
||||
winrt::com_ptr<IZoneSet> m_activeZoneSet;
|
||||
std::vector<winrt::com_ptr<IZoneSet>> m_zoneSets;
|
||||
winrt::com_ptr<IZone> m_highlightZone;
|
||||
std::vector<int> m_highlightZone;
|
||||
WPARAM m_keyLast{};
|
||||
size_t m_keyCycle{};
|
||||
static const UINT m_showAnimationDuration = 200; // ms
|
||||
@@ -284,12 +292,10 @@ ZoneWindow::ZoneWindow(HINSTANCE hinstance)
|
||||
|
||||
ZoneWindow::~ZoneWindow()
|
||||
{
|
||||
RestoreOrginalTransparency();
|
||||
|
||||
Gdiplus::GdiplusShutdown(gdiplusToken);
|
||||
}
|
||||
|
||||
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones)
|
||||
bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea)
|
||||
{
|
||||
m_host.copy_from(host);
|
||||
|
||||
@@ -308,7 +314,7 @@ bool ZoneWindow::Init(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monit
|
||||
|
||||
m_uniqueId = uniqueId;
|
||||
LoadSettings();
|
||||
InitializeZoneSets(mi);
|
||||
InitializeZoneSets(newWorkArea);
|
||||
|
||||
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)
|
||||
@@ -346,8 +352,6 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
|
||||
|
||||
if (m_host->isMakeDraggedWindowTransparentActive())
|
||||
{
|
||||
RestoreOrginalTransparency();
|
||||
|
||||
draggedWindowExstyle = GetWindowLong(window, GWL_EXSTYLE);
|
||||
|
||||
draggedWindow = window;
|
||||
@@ -363,7 +367,7 @@ IFACEMETHODIMP ZoneWindow::MoveSizeEnter(HWND window, bool dragEnabled) noexcept
|
||||
m_dragEnabled = dragEnabled;
|
||||
m_windowMoveSize = window;
|
||||
m_drawHints = true;
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
ShowZoneWindow();
|
||||
return S_OK;
|
||||
}
|
||||
@@ -378,13 +382,13 @@ IFACEMETHODIMP ZoneWindow::MoveSizeUpdate(POINT const& ptScreen, bool dragEnable
|
||||
|
||||
if (dragEnabled)
|
||||
{
|
||||
auto highlightZone = ZoneFromPoint(ptClient);
|
||||
auto highlightZone = ZonesFromPoint(ptClient);
|
||||
redraw = (highlightZone != m_highlightZone);
|
||||
m_highlightZone = std::move(highlightZone);
|
||||
}
|
||||
else if (m_highlightZone)
|
||||
else if (m_highlightZone.size())
|
||||
{
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
redraw = true;
|
||||
}
|
||||
|
||||
@@ -432,10 +436,16 @@ ZoneWindow::RestoreOrginalTransparency() noexcept
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::MoveWindowIntoZoneByIndex(HWND window, int index) noexcept
|
||||
{
|
||||
MoveWindowIntoZoneByIndexSet(window, { index });
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(void)
|
||||
ZoneWindow::MoveWindowIntoZoneByIndexSet(HWND window, const std::vector<int>& indexSet) noexcept
|
||||
{
|
||||
if (m_activeZoneSet)
|
||||
{
|
||||
m_activeZoneSet->MoveWindowIntoZoneByIndex(window, m_window.get(), index);
|
||||
m_activeZoneSet->MoveWindowIntoZoneByIndexSet(window, m_window.get(), indexSet, false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -518,7 +528,7 @@ ZoneWindow::HideZoneWindow() noexcept
|
||||
m_keyLast = 0;
|
||||
m_windowMoveSize = nullptr;
|
||||
m_drawHints = false;
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -529,10 +539,10 @@ void ZoneWindow::LoadSettings() noexcept
|
||||
JSONHelpers::FancyZonesDataInstance().AddDevice(m_uniqueId);
|
||||
}
|
||||
|
||||
void ZoneWindow::InitializeZoneSets(MONITORINFO const& mi) noexcept
|
||||
void ZoneWindow::InitializeZoneSets(bool newWorkArea) noexcept
|
||||
{
|
||||
auto parent = m_host->GetParentZoneWindow(m_monitor);
|
||||
if (parent)
|
||||
if (newWorkArea && parent)
|
||||
{
|
||||
// Update device info with device info from parent virtual desktop (if empty).
|
||||
JSONHelpers::FancyZonesDataInstance().CloneDeviceInfo(parent->UniqueId(), m_uniqueId);
|
||||
@@ -685,13 +695,13 @@ void ZoneWindow::OnKeyUp(WPARAM wparam) noexcept
|
||||
}
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZone> ZoneWindow::ZoneFromPoint(POINT pt) noexcept
|
||||
std::vector<int> ZoneWindow::ZonesFromPoint(POINT pt) noexcept
|
||||
{
|
||||
if (m_activeZoneSet)
|
||||
{
|
||||
return m_activeZoneSet->ZoneFromPoint(pt);
|
||||
return m_activeZoneSet->ZonesFromPoint(pt);
|
||||
}
|
||||
return nullptr;
|
||||
return {};
|
||||
}
|
||||
|
||||
void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::InputMode mode) noexcept
|
||||
@@ -739,7 +749,7 @@ void ZoneWindow::CycleActiveZoneSetInternal(DWORD wparam, Trace::ZoneWindow::Inp
|
||||
{
|
||||
m_host->MoveWindowsOnActiveZoneSetChange();
|
||||
}
|
||||
m_highlightZone = nullptr;
|
||||
m_highlightZone = {};
|
||||
}
|
||||
|
||||
void ZoneWindow::FlashZones() noexcept
|
||||
@@ -773,10 +783,10 @@ LRESULT CALLBACK ZoneWindow::s_WndProc(HWND window, UINT message, WPARAM wparam,
|
||||
DefWindowProc(window, message, wparam, lparam);
|
||||
}
|
||||
|
||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones) noexcept
|
||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor, const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept
|
||||
{
|
||||
auto self = winrt::make_self<ZoneWindow>(hinstance);
|
||||
if (self->Init(host, hinstance, monitor, uniqueId, flashZones))
|
||||
if (self->Init(host, hinstance, monitor, uniqueId, flashZones, newWorkArea))
|
||||
{
|
||||
return self;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ namespace ZoneWindowUtils
|
||||
const std::wstring& GetActiveZoneSetTmpPath();
|
||||
const std::wstring& GetAppliedZoneSetTmpPath();
|
||||
const std::wstring& GetCustomZoneSetsTmpPath();
|
||||
|
||||
std::wstring GenerateUniqueId(HMONITOR monitor, PCWSTR deviceId, PCWSTR virtualDesktopId);
|
||||
}
|
||||
|
||||
@@ -53,6 +54,13 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
* @param index Zone index within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndex)(HWND window, int index) = 0;
|
||||
/**
|
||||
* Assign window to the zones based on the set of zone indices inside zone layout.
|
||||
*
|
||||
* @param window Handle of window which should be assigned to zone.
|
||||
* @param indexSet The set of zone indices within zone layout.
|
||||
*/
|
||||
IFACEMETHOD_(void, MoveWindowIntoZoneByIndexSet)(HWND window, const std::vector<int>& indexSet) = 0;
|
||||
/**
|
||||
* Assign window to the zone based on direction (using WIN + LEFT/RIGHT arrow).
|
||||
*
|
||||
@@ -98,4 +106,4 @@ interface __declspec(uuid("{7F017528-8110-4FB3-BE41-F472969C2560}")) IZoneWindow
|
||||
};
|
||||
|
||||
winrt::com_ptr<IZoneWindow> MakeZoneWindow(IZoneWindowHost* host, HINSTANCE hinstance, HMONITOR monitor,
|
||||
const std::wstring& uniqueId, bool flashZones) noexcept;
|
||||
const std::wstring& uniqueId, bool flashZones, bool newWorkArea) noexcept;
|
||||
|
||||
@@ -108,3 +108,30 @@ void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo)
|
||||
|
||||
monitorInfo = std::move(sortedMonitorInfo);
|
||||
}
|
||||
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept
|
||||
{
|
||||
WINDOWPLACEMENT placement{};
|
||||
::GetWindowPlacement(window, &placement);
|
||||
|
||||
//wait if SW_SHOWMINIMIZED would be removed from window (Issue #1685)
|
||||
for (int i = 0; i < 5 && (placement.showCmd & SW_SHOWMINIMIZED) != 0; i++)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
::GetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
// Do not restore minimized windows. We change their placement though so they restore to the correct zone.
|
||||
if ((placement.showCmd & SW_SHOWMINIMIZED) == 0)
|
||||
{
|
||||
placement.showCmd = SW_RESTORE | SW_SHOWNA;
|
||||
}
|
||||
|
||||
placement.rcNormalPosition = rect;
|
||||
placement.flags |= WPF_ASYNCWINDOWPLACEMENT;
|
||||
|
||||
::SetWindowPlacement(window, &placement);
|
||||
// Do it again, allowing Windows to resize the window and set correct scaling
|
||||
// This fixes Issue #365
|
||||
::SetWindowPlacement(window, &placement);
|
||||
}
|
||||
|
||||
@@ -118,3 +118,4 @@ inline BYTE OpacitySettingToAlpha(int opacity)
|
||||
|
||||
UINT GetDpiForMonitor(HMONITOR monitor) noexcept;
|
||||
void OrderMonitors(std::vector<std::pair<HMONITOR, RECT>>& monitorInfo);
|
||||
void SizeWindowToRect(HWND window, RECT rect) noexcept;
|
||||
|
||||
@@ -132,8 +132,8 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD (ZoneFromPointEmpty)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointInner)
|
||||
@@ -146,9 +146,9 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
for (int j = top + 1; j < bottom; j++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, j });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, j });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -161,29 +161,29 @@ namespace FancyZonesUnitTests
|
||||
|
||||
for (int i = left; i < right; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, top });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, top });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
for (int i = top; i < bottom; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ left, i });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(expected, actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ left, i });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(expected, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
//bottom and right borders considered to be outside
|
||||
for (int i = left; i < right; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ i, bottom });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ i, bottom });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
for (int i = top; i < bottom; i++)
|
||||
{
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ right, i });
|
||||
Assert::IsTrue(nullptr == actual);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ right, i });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -193,8 +193,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ left, top, right, bottom });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 101, 101 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 200, 200 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointOverlapping)
|
||||
@@ -208,9 +208,65 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 10, 10, 50, 50 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual != nullptr);
|
||||
compareZones(zone2, actual);
|
||||
// zone4 is expected because it's the smallest one, and it's considered to be inside
|
||||
// since Multizones support
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual.size() == 1);
|
||||
compareZones(zone4, m_set->GetZones()[actual[0]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointMultizoneHorizontal)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 100 });
|
||||
Assert::IsTrue(actual.size() == 2);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone3, m_set->GetZones()[actual[1]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointMultizoneVertical)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 100, 50 });
|
||||
Assert::IsTrue(actual.size() == 2);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone2, m_set->GetZones()[actual[1]]);
|
||||
}
|
||||
|
||||
TEST_METHOD(ZoneFromPointMultizoneQuad)
|
||||
{
|
||||
winrt::com_ptr<IZone> zone1 = MakeZone({ 0, 0, 100, 100 });
|
||||
m_set->AddZone(zone1);
|
||||
winrt::com_ptr<IZone> zone2 = MakeZone({ 100, 0, 200, 100 });
|
||||
m_set->AddZone(zone2);
|
||||
winrt::com_ptr<IZone> zone3 = MakeZone({ 0, 100, 100, 200 });
|
||||
m_set->AddZone(zone3);
|
||||
winrt::com_ptr<IZone> zone4 = MakeZone({ 100, 100, 200, 200 });
|
||||
m_set->AddZone(zone4);
|
||||
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 100, 100 });
|
||||
Assert::IsTrue(actual.size() == 4);
|
||||
compareZones(zone1, m_set->GetZones()[actual[0]]);
|
||||
compareZones(zone2, m_set->GetZones()[actual[1]]);
|
||||
compareZones(zone3, m_set->GetZones()[actual[2]]);
|
||||
compareZones(zone4, m_set->GetZones()[actual[3]]);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointWithNotNormalizedRect)
|
||||
@@ -218,8 +274,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ 100, 100, 0, 0 });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 50, 50 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneFromPointWithZeroRect)
|
||||
@@ -227,8 +283,8 @@ namespace FancyZonesUnitTests
|
||||
winrt::com_ptr<IZone> zone = MakeZone({ 0, 0, 0, 0 });
|
||||
m_set->AddZone(zone);
|
||||
|
||||
auto actual = m_set->ZoneFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual == nullptr);
|
||||
auto actual = m_set->ZonesFromPoint(POINT{ 0, 0 });
|
||||
Assert::IsTrue(actual.size() == 0);
|
||||
}
|
||||
|
||||
TEST_METHOD (ZoneIndexFromWindow)
|
||||
@@ -316,7 +372,7 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsTrue(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
@@ -325,7 +381,7 @@ namespace FancyZonesUnitTests
|
||||
TEST_METHOD (MoveWindowIntoZoneByIndexWithNoZones)
|
||||
{
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
}
|
||||
|
||||
TEST_METHOD (MoveWindowIntoZoneByIndexWithInvalidIndex)
|
||||
@@ -338,8 +394,8 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100);
|
||||
Assert::IsTrue(zone1->ContainsWindow(window));
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 100, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
}
|
||||
@@ -355,17 +411,17 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
Assert::IsTrue(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 1, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsTrue(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 2, false);
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsTrue(zone3->ContainsWindow(window));
|
||||
@@ -382,9 +438,9 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone3);
|
||||
|
||||
HWND window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
m_set->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
Assert::IsTrue(zone1->ContainsWindow(window));
|
||||
Assert::IsFalse(zone2->ContainsWindow(window));
|
||||
Assert::IsFalse(zone3->ContainsWindow(window));
|
||||
@@ -401,7 +457,7 @@ namespace FancyZonesUnitTests
|
||||
m_set->AddZone(zone1);
|
||||
|
||||
auto window = Mocks::Window();
|
||||
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 101, 101 });
|
||||
m_set->MoveWindowIntoZoneByPoint(window, Mocks::Window(), POINT{ 200, 200 });
|
||||
|
||||
Assert::IsFalse(zone1->ContainsWindow(window));
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
const std::wstring m_deviceId = L"\\\\?\\DISPLAY#DELA026#5&10a58c63&0&UID16777488#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}";
|
||||
const std::wstring m_virtualDesktopId = L"MyVirtualDesktopId";
|
||||
std::wstringstream m_parentUniqueId;
|
||||
std::wstringstream m_uniqueId;
|
||||
|
||||
HINSTANCE m_hInst{};
|
||||
@@ -75,6 +76,7 @@ namespace FancyZonesUnitTests
|
||||
m_monitorInfo.cbSize = sizeof(m_monitorInfo);
|
||||
Assert::AreNotEqual(0, GetMonitorInfoW(m_monitor, &m_monitorInfo));
|
||||
|
||||
m_parentUniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{61FA9FC0-26A6-4B37-A834-491C148DFC57}";
|
||||
m_uniqueId << L"DELA026#5&10a58c63&0&UID16777488_" << m_monitorInfo.rcMonitor.right << "_" << m_monitorInfo.rcMonitor.bottom << "_{39B25DD2-130D-4B5D-8851-4791D66B1539}";
|
||||
|
||||
Assert::IsFalse(ZoneWindowUtils::GetActiveZoneSetTmpPath().empty());
|
||||
@@ -113,7 +115,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||
|
||||
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
return MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
}
|
||||
|
||||
void testZoneWindow(winrt::com_ptr<IZoneWindow> zoneWindow)
|
||||
@@ -129,14 +131,14 @@ namespace FancyZonesUnitTests
|
||||
public:
|
||||
TEST_METHOD(CreateZoneWindow)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
testZoneWindow(m_zoneWindow);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
}
|
||||
|
||||
TEST_METHOD(CreateZoneWindowNoHinst)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(m_zoneWindow);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
@@ -144,7 +146,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(CreateZoneWindowNoHinstFlashZones)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, {}, m_monitor, m_uniqueId.str(), true, false);
|
||||
|
||||
testZoneWindow(m_zoneWindow);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
@@ -152,7 +154,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(CreateZoneWindowNoMonitor)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), false, false);
|
||||
|
||||
Assert::IsNull(m_zoneWindow.get());
|
||||
Assert::IsNotNull(m_hostPtr);
|
||||
@@ -160,7 +162,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(CreateZoneWindowNoMonitorFlashZones)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, {}, m_uniqueId.str(), true, false);
|
||||
|
||||
Assert::IsNull(m_zoneWindow.get());
|
||||
Assert::IsNotNull(m_hostPtr);
|
||||
@@ -170,7 +172,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// Generate unique id without device id
|
||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, nullptr, m_virtualDesktopId.c_str());
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
|
||||
|
||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||
const std::wstring expectedUniqueId = L"FallbackDevice_" + std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom) + L"_" + m_virtualDesktopId;
|
||||
@@ -186,7 +188,7 @@ namespace FancyZonesUnitTests
|
||||
{
|
||||
// Generate unique id without virtual desktop id
|
||||
std::wstring uniqueId = ZoneWindowUtils::GenerateUniqueId(m_monitor, m_deviceId.c_str(), nullptr);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, uniqueId, false, false);
|
||||
|
||||
const std::wstring expectedWorkArea = std::to_wstring(m_monitorInfo.rcMonitor.right) + L"_" + std::to_wstring(m_monitorInfo.rcMonitor.bottom);
|
||||
Assert::IsNotNull(m_zoneWindow.get());
|
||||
@@ -213,7 +215,7 @@ namespace FancyZonesUnitTests
|
||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||
|
||||
//temp file read on initialization
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(actual);
|
||||
|
||||
@@ -237,7 +239,7 @@ namespace FancyZonesUnitTests
|
||||
m_fancyZonesData.ParseDeviceInfoFromTmpFile(activeZoneSetTempPath);
|
||||
|
||||
//temp file read on initialization
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(actual);
|
||||
|
||||
@@ -273,7 +275,7 @@ namespace FancyZonesUnitTests
|
||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||
|
||||
//temp file read on initialization
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(actual);
|
||||
|
||||
@@ -320,7 +322,7 @@ namespace FancyZonesUnitTests
|
||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||
|
||||
//temp file read on initialization
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(actual);
|
||||
|
||||
@@ -367,7 +369,7 @@ namespace FancyZonesUnitTests
|
||||
m_fancyZonesData.ParseCustomZoneSetFromTmpFile(appliedZoneSetTempPath);
|
||||
|
||||
//temp file read on initialization
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
auto actual = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
testZoneWindow(actual);
|
||||
|
||||
@@ -376,9 +378,68 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual((size_t)1, actualZoneSet.size());
|
||||
}
|
||||
|
||||
TEST_METHOD (CreateZoneWindowClonedFromParent)
|
||||
{
|
||||
using namespace JSONHelpers;
|
||||
|
||||
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
|
||||
const int spacing = 10;
|
||||
const int zoneCount = 5;
|
||||
const auto customSetGuid = Helpers::CreateGuidString();
|
||||
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
|
||||
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
|
||||
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
|
||||
|
||||
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
|
||||
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
|
||||
|
||||
// newWorkArea = true - zoneWindow will be cloned from parent
|
||||
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, true);
|
||||
|
||||
Assert::IsNotNull(actualZoneWindow->ActiveZoneSet());
|
||||
const auto actualZoneSet = actualZoneWindow->ActiveZoneSet()->GetZones();
|
||||
Assert::AreEqual((size_t)zoneCount, actualZoneSet.size());
|
||||
|
||||
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
|
||||
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
|
||||
Assert::AreEqual(zoneCount, currentDeviceInfo.zoneCount);
|
||||
Assert::AreEqual(spacing, currentDeviceInfo.spacing);
|
||||
Assert::AreEqual(static_cast<int>(type), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
|
||||
}
|
||||
|
||||
TEST_METHOD (CreateZoneWindowNotClonedFromParent)
|
||||
{
|
||||
using namespace JSONHelpers;
|
||||
|
||||
const ZoneSetLayoutType type = ZoneSetLayoutType::PriorityGrid;
|
||||
const int spacing = 10;
|
||||
const int zoneCount = 5;
|
||||
const auto customSetGuid = Helpers::CreateGuidString();
|
||||
const auto parentZoneSet = ZoneSetData{ customSetGuid, type };
|
||||
const auto parentDeviceInfo = DeviceInfoData{ parentZoneSet, true, spacing, zoneCount };
|
||||
m_fancyZonesData.SetDeviceInfo(m_parentUniqueId.str(), parentDeviceInfo);
|
||||
|
||||
auto parentZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_parentUniqueId.str(), false, false);
|
||||
m_zoneWindowHost.m_zoneWindow = parentZoneWindow.get();
|
||||
|
||||
// newWorkArea = false - zoneWindow won't be cloned from parent
|
||||
auto actualZoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
Assert::IsNull(actualZoneWindow->ActiveZoneSet());
|
||||
|
||||
Assert::IsTrue(m_fancyZonesData.GetDeviceInfoMap().contains(m_uniqueId.str()));
|
||||
auto currentDeviceInfo = m_fancyZonesData.GetDeviceInfoMap().at(m_uniqueId.str());
|
||||
// default values
|
||||
Assert::AreEqual(false, currentDeviceInfo.showSpacing);
|
||||
Assert::AreEqual(0, currentDeviceInfo.zoneCount);
|
||||
Assert::AreEqual(0, currentDeviceInfo.spacing);
|
||||
Assert::AreEqual(std::wstring{ L"null" }, currentDeviceInfo.activeZoneSet.uuid);
|
||||
Assert::AreEqual(static_cast<int>(ZoneSetLayoutType::Blank), static_cast<int>(currentDeviceInfo.activeZoneSet.type));
|
||||
}
|
||||
|
||||
TEST_METHOD(MoveSizeEnter)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = m_zoneWindow->MoveSizeEnter(Mocks::Window(), true);
|
||||
@@ -389,7 +450,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeEnterTwice)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = E_INVALIDARG;
|
||||
|
||||
@@ -402,7 +463,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeUpdate)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ 0, 0 }, true);
|
||||
@@ -413,7 +474,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeUpdatePointNegativeCoordinates)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ -10, -10 }, true);
|
||||
@@ -424,7 +485,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeUpdatePointBigCoordinates)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = m_zoneWindow->MoveSizeUpdate(POINT{ m_monitorInfo.rcMonitor.right + 1, m_monitorInfo.rcMonitor.bottom + 1 }, true);
|
||||
@@ -445,7 +506,7 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||
Assert::AreNotEqual(-1, actualZoneIndex);
|
||||
}
|
||||
@@ -458,7 +519,7 @@ namespace FancyZonesUnitTests
|
||||
zoneWindow->MoveSizeEnter(window, true);
|
||||
|
||||
const auto expected = S_OK;
|
||||
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ 0, 0 });
|
||||
const auto actual = zoneWindow->MoveSizeEnd(window, POINT{ -100, -100 });
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
@@ -468,7 +529,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeEndDifferentWindows)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto window = Mocks::Window();
|
||||
m_zoneWindow->MoveSizeEnter(window, true);
|
||||
@@ -481,7 +542,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveSizeEndWindowNotSet)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
|
||||
const auto expected = E_INVALIDARG;
|
||||
const auto actual = m_zoneWindow->MoveSizeEnd(Mocks::Window(), POINT{ 0, 0 });
|
||||
@@ -501,14 +562,14 @@ namespace FancyZonesUnitTests
|
||||
Assert::AreEqual(expected, actual);
|
||||
|
||||
const auto zoneSet = zoneWindow->ActiveZoneSet();
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), false);
|
||||
zoneSet->MoveWindowIntoZoneByIndex(window, Mocks::Window(), 0, false);
|
||||
const auto actualZoneIndex = zoneSet->GetZoneIndexFromWindow(window);
|
||||
Assert::AreNotEqual(-1, actualZoneIndex); //with invalid point zone remains the same
|
||||
}
|
||||
|
||||
TEST_METHOD(MoveWindowIntoZoneByIndexNoActiveZoneSet)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
|
||||
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||
@@ -526,7 +587,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(MoveWindowIntoZoneByDirectionNoActiveZoneSet)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
|
||||
m_zoneWindow->MoveWindowIntoZoneByIndex(Mocks::Window(), 0);
|
||||
@@ -564,7 +625,7 @@ namespace FancyZonesUnitTests
|
||||
|
||||
TEST_METHOD(SaveWindowProcessToZoneIndexNoActiveZoneSet)
|
||||
{
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false);
|
||||
m_zoneWindow = MakeZoneWindow(m_hostPtr, m_hInst, m_monitor, m_uniqueId.str(), false, false);
|
||||
Assert::IsNull(m_zoneWindow->ActiveZoneSet());
|
||||
|
||||
m_zoneWindow->SaveWindowProcessToZoneIndex(Mocks::Window());
|
||||
@@ -650,5 +711,29 @@ namespace FancyZonesUnitTests
|
||||
const auto actual = m_fancyZonesData.GetAppZoneHistoryMap().at(processPath).zoneIndex;
|
||||
Assert::AreEqual(expected, actual);
|
||||
}
|
||||
|
||||
TEST_METHOD (WhenWindowIsNotResizablePlacingItIntoTheZoneShouldNotResizeIt)
|
||||
{
|
||||
m_zoneWindow = InitZoneWindowWithActiveZoneSet();
|
||||
Assert::IsNotNull(m_zoneWindow->ActiveZoneSet());
|
||||
|
||||
auto window = Mocks::WindowCreate(m_hInst);
|
||||
|
||||
int orginalWidth = 450;
|
||||
int orginalHeight = 550;
|
||||
|
||||
SetWindowPos(window, nullptr, 150, 150, orginalWidth, orginalHeight, SWP_SHOWWINDOW);
|
||||
SetWindowLong(window, GWL_STYLE, GetWindowLong(window, GWL_STYLE) & ~WS_SIZEBOX);
|
||||
|
||||
auto zone = MakeZone(RECT{ 50, 50, 300, 300 });
|
||||
m_zoneWindow->ActiveZoneSet()->AddZone(zone);
|
||||
|
||||
m_zoneWindow->MoveWindowIntoZoneByDirection(window, VK_LEFT, true);
|
||||
|
||||
RECT inZoneRect;
|
||||
GetWindowRect(window, &inZoneRect);
|
||||
Assert::AreEqual(orginalWidth, (int)inZoneRect.right - (int) inZoneRect.left);
|
||||
Assert::AreEqual(orginalHeight, (int)inZoneRect.bottom - (int)inZoneRect.top);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -147,6 +147,7 @@ HRESULT CContextMenuHandler::QueryContextMenu(_In_ HMENU hmenu, UINT indexMenu,
|
||||
if (!InsertMenuItem(hmenu, indexMenu, TRUE, &mii))
|
||||
{
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
Trace::QueryContextMenuError(hr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -220,12 +221,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
|
||||
HRESULT hr = E_FAIL;
|
||||
if (!CreatePipe(&hReadPipe, &hWritePipe, &sa, 0))
|
||||
{
|
||||
Trace::InvokedRet(hr);
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
if (!SetHandleInformation(hWritePipe, HANDLE_FLAG_INHERIT, 0))
|
||||
{
|
||||
Trace::InvokedRet(hr);
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
CAtlFile writePipe(hWritePipe);
|
||||
@@ -277,12 +278,12 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
|
||||
delete[] lpszCommandLine;
|
||||
if (!CloseHandle(processInformation.hProcess))
|
||||
{
|
||||
Trace::InvokedRet(hr);
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
if (!CloseHandle(processInformation.hThread))
|
||||
{
|
||||
Trace::InvokedRet(hr);
|
||||
hr = HRESULT_FROM_WIN32(GetLastError());
|
||||
return hr;
|
||||
}
|
||||
|
||||
@@ -322,7 +323,6 @@ HRESULT CContextMenuHandler::ResizePictures(CMINVOKECOMMANDINFO* pici, IShellIte
|
||||
|
||||
writePipe.Close();
|
||||
hr = S_OK;
|
||||
Trace::InvokedRet(hr);
|
||||
return hr;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,3 +47,13 @@ void Trace::InvokedRet(_In_ HRESULT hr) noexcept
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
void Trace::QueryContextMenuError(_In_ HRESULT hr) noexcept
|
||||
{
|
||||
TraceLoggingWrite(
|
||||
g_hProvider,
|
||||
"ImageResizer_QueryContextMenuError",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingHResult(hr),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE));
|
||||
}
|
||||
|
||||
@@ -8,4 +8,5 @@ public:
|
||||
static void EnableImageResizer(_In_ bool enabled) noexcept;
|
||||
static void Invoked() noexcept;
|
||||
static void InvokedRet(_In_ HRESULT hr) noexcept;
|
||||
static void QueryContextMenuError(_In_ HRESULT hr) noexcept;
|
||||
};
|
||||
@@ -48,7 +48,7 @@ HRESULT CPowerRenameMenu::s_CreateInstance(_In_opt_ IUnknown*, _In_ REFIID riid,
|
||||
HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObject* pdtobj, HKEY)
|
||||
{
|
||||
// Check if we have disabled ourselves
|
||||
if (!CSettings::GetEnabled())
|
||||
if (!CSettingsInstance().GetEnabled())
|
||||
return E_FAIL;
|
||||
|
||||
// Cache the data object to be used later
|
||||
@@ -60,11 +60,11 @@ HRESULT CPowerRenameMenu::Initialize(_In_opt_ PCIDLIST_ABSOLUTE, _In_ IDataObjec
|
||||
HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirst, UINT, UINT uFlags)
|
||||
{
|
||||
// Check if we have disabled ourselves
|
||||
if (!CSettings::GetEnabled())
|
||||
if (!CSettingsInstance().GetEnabled())
|
||||
return E_FAIL;
|
||||
|
||||
// Check if we should only be on the extended context menu
|
||||
if (CSettings::GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS)))
|
||||
if (CSettingsInstance().GetExtendedContextMenuOnly() && (!(uFlags & CMF_EXTENDEDVERBS)))
|
||||
return E_FAIL;
|
||||
|
||||
HRESULT hr = E_UNEXPECTED;
|
||||
@@ -81,7 +81,7 @@ HRESULT CPowerRenameMenu::QueryContextMenu(HMENU hMenu, UINT index, UINT uIDFirs
|
||||
mii.dwTypeData = (PWSTR)menuName;
|
||||
mii.fState = MFS_ENABLED;
|
||||
|
||||
if (CSettings::GetShowIconOnMenu())
|
||||
if (CSettingsInstance().GetShowIconOnMenu())
|
||||
{
|
||||
HICON hIcon = (HICON)LoadImage(g_hInst, MAKEINTRESOURCE(IDI_RENAME), IMAGE_ICON, 16, 16, 0);
|
||||
if (hIcon)
|
||||
@@ -113,7 +113,7 @@ HRESULT CPowerRenameMenu::InvokeCommand(_In_ LPCMINVOKECOMMANDINFO pici)
|
||||
{
|
||||
HRESULT hr = E_FAIL;
|
||||
|
||||
if (CSettings::GetEnabled() &&
|
||||
if (CSettingsInstance().GetEnabled() &&
|
||||
(IS_INTRESOURCE(pici->lpVerb)) &&
|
||||
(LOWORD(pici->lpVerb) == 0))
|
||||
{
|
||||
@@ -203,7 +203,7 @@ HRESULT __stdcall CPowerRenameMenu::GetTitle(IShellItemArray* /*psiItemArray*/,
|
||||
|
||||
HRESULT __stdcall CPowerRenameMenu::GetIcon(IShellItemArray* /*psiItemArray*/, LPWSTR* ppszIcon)
|
||||
{
|
||||
if (!CSettings::GetShowIconOnMenu())
|
||||
if (!CSettingsInstance().GetShowIconOnMenu())
|
||||
{
|
||||
*ppszIcon = nullptr;
|
||||
return E_NOTIMPL;
|
||||
@@ -229,7 +229,7 @@ HRESULT __stdcall CPowerRenameMenu::GetCanonicalName(GUID* pguidCommandName)
|
||||
|
||||
HRESULT __stdcall CPowerRenameMenu::GetState(IShellItemArray* psiItemArray, BOOL fOkToBeSlow, EXPCMDSTATE* pCmdState)
|
||||
{
|
||||
*pCmdState = CSettings::GetEnabled() ? ECS_ENABLED : ECS_HIDDEN;
|
||||
*pCmdState = CSettingsInstance().GetEnabled() ? ECS_ENABLED : ECS_HIDDEN;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<AdditionalDependencies>Pathcch.lib;comctl32.lib;$(SolutionDir)$(Platform)\$(Configuration)\obj\PowerRenameUI\PowerRenameUI.res;shcore.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<ModuleDefinitionFile>PowerRenameExt.def</ModuleDefinitionFile>
|
||||
<DelayLoadDLLs>gdi32.dll;advapi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
<DelayLoadDLLs>gdi32.dll;shell32.dll;ole32.dll;shlwapi.dll;oleaut32.dll;%(DelayLoadDLLs)</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -207,17 +207,17 @@ public:
|
||||
settings.add_bool_toogle(
|
||||
L"bool_persist_input",
|
||||
GET_RESOURCE_STRING(IDS_RESTORE_SEARCH),
|
||||
CSettings::GetPersistState());
|
||||
CSettingsInstance().GetPersistState());
|
||||
|
||||
settings.add_bool_toogle(
|
||||
L"bool_mru_enabled",
|
||||
GET_RESOURCE_STRING(IDS_ENABLE_AUTO),
|
||||
CSettings::GetMRUEnabled());
|
||||
CSettingsInstance().GetMRUEnabled());
|
||||
|
||||
settings.add_int_spinner(
|
||||
L"int_max_mru_size",
|
||||
GET_RESOURCE_STRING(IDS_MAX_ITEMS),
|
||||
CSettings::GetMaxMRUSize(),
|
||||
CSettingsInstance().GetMaxMRUSize(),
|
||||
0,
|
||||
20,
|
||||
1);
|
||||
@@ -225,12 +225,12 @@ public:
|
||||
settings.add_bool_toogle(
|
||||
L"bool_show_icon_on_menu",
|
||||
GET_RESOURCE_STRING(IDS_ICON_CONTEXT_MENU),
|
||||
CSettings::GetShowIconOnMenu());
|
||||
CSettingsInstance().GetShowIconOnMenu());
|
||||
|
||||
settings.add_bool_toogle(
|
||||
L"bool_show_extended_menu",
|
||||
GET_RESOURCE_STRING(IDS_EXTENDED_MENU_INFO),
|
||||
CSettings::GetExtendedContextMenuOnly());
|
||||
CSettingsInstance().GetExtendedContextMenuOnly());
|
||||
|
||||
return settings.serialize_to_buffer(buffer, buffer_size);
|
||||
}
|
||||
@@ -245,11 +245,12 @@ public:
|
||||
PowerToysSettings::PowerToyValues values =
|
||||
PowerToysSettings::PowerToyValues::from_json_string(config);
|
||||
|
||||
CSettings::SetPersistState(values.get_bool_value(L"bool_persist_input").value());
|
||||
CSettings::SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value());
|
||||
CSettings::SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value());
|
||||
CSettings::SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value());
|
||||
CSettings::SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value());
|
||||
CSettingsInstance().SetPersistState(values.get_bool_value(L"bool_persist_input").value());
|
||||
CSettingsInstance().SetMRUEnabled(values.get_bool_value(L"bool_mru_enabled").value());
|
||||
CSettingsInstance().SetMaxMRUSize(values.get_int_value(L"int_max_mru_size").value());
|
||||
CSettingsInstance().SetShowIconOnMenu(values.get_bool_value(L"bool_show_icon_on_menu").value());
|
||||
CSettingsInstance().SetExtendedContextMenuOnly(values.get_bool_value(L"bool_show_extended_menu").value());
|
||||
CSettingsInstance().Save();
|
||||
|
||||
Trace::SettingsChanged();
|
||||
}
|
||||
@@ -282,13 +283,14 @@ public:
|
||||
|
||||
void init_settings()
|
||||
{
|
||||
m_enabled = CSettings::GetEnabled();
|
||||
m_enabled = CSettingsInstance().GetEnabled();
|
||||
Trace::EnablePowerRename(m_enabled);
|
||||
}
|
||||
|
||||
void save_settings()
|
||||
{
|
||||
CSettings::SetEnabled(m_enabled);
|
||||
CSettingsInstance().SetEnabled(m_enabled);
|
||||
CSettingsInstance().Save();
|
||||
Trace::EnablePowerRename(m_enabled);
|
||||
}
|
||||
|
||||
|
||||
@@ -85,13 +85,14 @@
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -99,7 +100,7 @@
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
<WarningLevel>Level4</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<PreprocessorDefinitions>_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
@@ -107,6 +108,7 @@
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -116,7 +118,7 @@
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
@@ -124,6 +126,7 @@
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
@@ -134,7 +137,7 @@
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<PrecompiledHeader>Create</PrecompiledHeader>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
@@ -143,6 +146,7 @@
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>..\;..\..\..\common;..\..\..\common\telemetry;..\..\</AdditionalIncludeDirectories>
|
||||
<PrecompiledHeaderFile>stdafx.h</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
|
||||
@@ -1,186 +1,274 @@
|
||||
#include "stdafx.h"
|
||||
#include <commctrl.h>
|
||||
#include "Settings.h"
|
||||
#include "PowerRenameInterfaces.h"
|
||||
#include "settings_helpers.h"
|
||||
|
||||
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
|
||||
const wchar_t c_mruSearchRegPath[] = L"SearchMRU";
|
||||
const wchar_t c_mruReplaceRegPath[] = L"ReplaceMRU";
|
||||
#include <filesystem>
|
||||
#include <commctrl.h>
|
||||
#include <algorithm>
|
||||
|
||||
const wchar_t c_enabled[] = L"Enabled";
|
||||
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
|
||||
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
|
||||
const wchar_t c_persistState[] = L"PersistState";
|
||||
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
|
||||
const wchar_t c_flags[] = L"Flags";
|
||||
const wchar_t c_searchText[] = L"SearchText";
|
||||
const wchar_t c_replaceText[] = L"ReplaceText";
|
||||
const wchar_t c_mruEnabled[] = L"MRUEnabled";
|
||||
|
||||
const bool c_enabledDefault = true;
|
||||
const bool c_showIconOnMenuDefault = true;
|
||||
const bool c_extendedContextMenuOnlyDefaut = false;
|
||||
const bool c_persistStateDefault = true;
|
||||
const bool c_mruEnabledDefault = true;
|
||||
|
||||
const DWORD c_maxMRUSizeDefault = 10;
|
||||
const DWORD c_flagsDefault = 0;
|
||||
|
||||
bool CSettings::GetEnabled()
|
||||
namespace
|
||||
{
|
||||
return GetRegBoolValue(c_enabled, c_enabledDefault);
|
||||
}
|
||||
const wchar_t c_powerRenameDataFilePath[] = L"\\power-rename-settings.json";
|
||||
const wchar_t c_searchMRUListFilePath[] = L"\\search-mru.json";
|
||||
const wchar_t c_replaceMRUListFilePath[] = L"\\replace-mru.json";
|
||||
|
||||
bool CSettings::SetEnabled(_In_ bool enabled)
|
||||
{
|
||||
return SetRegBoolValue(c_enabled, enabled);
|
||||
}
|
||||
const wchar_t c_rootRegPath[] = L"Software\\Microsoft\\PowerRename";
|
||||
const wchar_t c_mruSearchRegPath[] = L"\\SearchMRU";
|
||||
const wchar_t c_mruReplaceRegPath[] = L"\\ReplaceMRU";
|
||||
|
||||
bool CSettings::GetShowIconOnMenu()
|
||||
{
|
||||
return GetRegBoolValue(c_showIconOnMenu, c_showIconOnMenuDefault);
|
||||
}
|
||||
const wchar_t c_enabled[] = L"Enabled";
|
||||
const wchar_t c_showIconOnMenu[] = L"ShowIcon";
|
||||
const wchar_t c_extendedContextMenuOnly[] = L"ExtendedContextMenuOnly";
|
||||
const wchar_t c_persistState[] = L"PersistState";
|
||||
const wchar_t c_maxMRUSize[] = L"MaxMRUSize";
|
||||
const wchar_t c_flags[] = L"Flags";
|
||||
const wchar_t c_searchText[] = L"SearchText";
|
||||
const wchar_t c_replaceText[] = L"ReplaceText";
|
||||
const wchar_t c_mruEnabled[] = L"MRUEnabled";
|
||||
const wchar_t c_mruList[] = L"MRUList";
|
||||
const wchar_t c_insertionIdx[] = L"InsertionIdx";
|
||||
|
||||
bool CSettings::SetShowIconOnMenu(_In_ bool show)
|
||||
{
|
||||
return SetRegBoolValue(c_showIconOnMenu, show);
|
||||
}
|
||||
|
||||
bool CSettings::GetExtendedContextMenuOnly()
|
||||
{
|
||||
return GetRegBoolValue(c_extendedContextMenuOnly, c_extendedContextMenuOnlyDefaut);
|
||||
}
|
||||
|
||||
bool CSettings::SetExtendedContextMenuOnly(_In_ bool extendedOnly)
|
||||
{
|
||||
return SetRegBoolValue(c_extendedContextMenuOnly, extendedOnly);
|
||||
}
|
||||
|
||||
bool CSettings::GetPersistState()
|
||||
{
|
||||
return GetRegBoolValue(c_persistState, c_persistStateDefault);
|
||||
}
|
||||
|
||||
bool CSettings::SetPersistState(_In_ bool persistState)
|
||||
{
|
||||
return SetRegBoolValue(c_persistState, persistState);
|
||||
}
|
||||
|
||||
bool CSettings::GetMRUEnabled()
|
||||
{
|
||||
return GetRegBoolValue(c_mruEnabled, c_mruEnabledDefault);
|
||||
}
|
||||
|
||||
bool CSettings::SetMRUEnabled(_In_ bool enabled)
|
||||
{
|
||||
return SetRegBoolValue(c_mruEnabled, enabled);
|
||||
}
|
||||
|
||||
DWORD CSettings::GetMaxMRUSize()
|
||||
{
|
||||
return GetRegDWORDValue(c_maxMRUSize, c_maxMRUSizeDefault);
|
||||
}
|
||||
|
||||
bool CSettings::SetMaxMRUSize(_In_ DWORD maxMRUSize)
|
||||
{
|
||||
return SetRegDWORDValue(c_maxMRUSize, maxMRUSize);
|
||||
}
|
||||
|
||||
DWORD CSettings::GetFlags()
|
||||
{
|
||||
return GetRegDWORDValue(c_flags, c_flagsDefault);
|
||||
}
|
||||
|
||||
bool CSettings::SetFlags(_In_ DWORD flags)
|
||||
{
|
||||
return SetRegDWORDValue(c_flags, flags);
|
||||
}
|
||||
|
||||
bool CSettings::GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
|
||||
{
|
||||
return GetRegStringValue(c_searchText, text, cchBuf);
|
||||
}
|
||||
|
||||
bool CSettings::SetSearchText(_In_ PCWSTR text)
|
||||
{
|
||||
return SetRegStringValue(c_searchText, text);
|
||||
}
|
||||
|
||||
bool CSettings::GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf)
|
||||
{
|
||||
return GetRegStringValue(c_replaceText, text, cchBuf);
|
||||
}
|
||||
|
||||
bool CSettings::SetReplaceText(_In_ PCWSTR text)
|
||||
{
|
||||
return SetRegStringValue(c_replaceText, text);
|
||||
}
|
||||
|
||||
bool CSettings::SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value)
|
||||
{
|
||||
DWORD dwValue = value ? 1 : 0;
|
||||
return SetRegDWORDValue(valueName, dwValue);
|
||||
}
|
||||
|
||||
bool CSettings::GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue)
|
||||
{
|
||||
DWORD value = GetRegDWORDValue(valueName, (defaultValue == 0) ? false : true);
|
||||
return (value == 0) ? false : true;
|
||||
}
|
||||
|
||||
bool CSettings::SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value)
|
||||
{
|
||||
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_DWORD, &value, sizeof(value)))));
|
||||
}
|
||||
|
||||
DWORD CSettings::GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue)
|
||||
{
|
||||
DWORD retVal = defaultValue;
|
||||
DWORD type = REG_DWORD;
|
||||
DWORD dwEnabled = 0;
|
||||
DWORD cb = sizeof(dwEnabled);
|
||||
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, &dwEnabled, &cb) == ERROR_SUCCESS)
|
||||
long GetRegNumber(const std::wstring& valueName, long defaultValue)
|
||||
{
|
||||
retVal = dwEnabled;
|
||||
DWORD type = REG_DWORD;
|
||||
DWORD data = 0;
|
||||
DWORD size = sizeof(DWORD);
|
||||
if (SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), &type, &data, &size) == ERROR_SUCCESS)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
bool CSettings::SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value)
|
||||
{
|
||||
ULONG cb = (DWORD)((wcslen(value) + 1) * sizeof(*value));
|
||||
return (SUCCEEDED(HRESULT_FROM_WIN32(SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, REG_SZ, (const BYTE*)value, cb))));
|
||||
}
|
||||
|
||||
bool CSettings::GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf)
|
||||
{
|
||||
if (cchBuf > 0)
|
||||
void SetRegNumber(const std::wstring& valueName, long value)
|
||||
{
|
||||
SHSetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName.c_str(), REG_DWORD, &value, sizeof(value));
|
||||
}
|
||||
|
||||
bool GetRegBoolean(const std::wstring& valueName, bool defaultValue)
|
||||
{
|
||||
DWORD value = GetRegNumber(valueName.c_str(), defaultValue ? 1 : 0);
|
||||
return (value == 0) ? false : true;
|
||||
}
|
||||
|
||||
void SetRegBoolean(const std::wstring& valueName, bool value)
|
||||
{
|
||||
SetRegNumber(valueName, value ? 1 : 0);
|
||||
}
|
||||
|
||||
std::wstring GetRegString(const std::wstring& valueName,const std::wstring& subPath)
|
||||
{
|
||||
wchar_t value[CSettings::MAX_INPUT_STRING_LEN];
|
||||
value[0] = L'\0';
|
||||
DWORD type = REG_SZ;
|
||||
DWORD size = CSettings::MAX_INPUT_STRING_LEN * sizeof(wchar_t);
|
||||
std::wstring completePath = std::wstring(c_rootRegPath) + subPath;
|
||||
if (SHGetValue(HKEY_CURRENT_USER, completePath.c_str(), valueName.c_str(), &type, value, &size) == ERROR_SUCCESS)
|
||||
{
|
||||
return std::wstring(value);
|
||||
}
|
||||
return std::wstring{};
|
||||
}
|
||||
|
||||
DWORD type = REG_SZ;
|
||||
ULONG cb = cchBuf * sizeof(*value);
|
||||
return (SUCCEEDED(HRESULT_FROM_WIN32(SHGetValue(HKEY_CURRENT_USER, c_rootRegPath, valueName, &type, value, &cb) == ERROR_SUCCESS)));
|
||||
bool LastModifiedTime(const std::wstring& filePath, FILETIME* lpFileTime)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr{};
|
||||
if (GetFileAttributesExW(filePath.c_str(), GetFileExInfoStandard, &attr))
|
||||
{
|
||||
*lpFileTime = attr.ftLastWriteTime;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class MRUListHandler
|
||||
{
|
||||
public:
|
||||
MRUListHandler(int size, const std::wstring& filePath, const std::wstring& regPath) :
|
||||
pushIdx(0),
|
||||
nextIdx(1),
|
||||
size(size),
|
||||
jsonFilePath(PTSettingsHelper::get_module_save_folder_location(L"PowerRename") + filePath),
|
||||
registryFilePath(regPath)
|
||||
{
|
||||
items.resize(size);
|
||||
Load();
|
||||
}
|
||||
|
||||
typedef int (CALLBACK* MRUCMPPROC)(LPCWSTR, LPCWSTR);
|
||||
void Push(const std::wstring& data);
|
||||
bool Next(std::wstring& data);
|
||||
|
||||
typedef struct {
|
||||
DWORD cbSize;
|
||||
UINT uMax;
|
||||
UINT fFlags;
|
||||
HKEY hKey;
|
||||
LPCTSTR lpszSubKey;
|
||||
MRUCMPPROC lpfnCompare;
|
||||
} MRUINFO;
|
||||
void Reset();
|
||||
|
||||
typedef HANDLE (*CreateMRUListFn)(MRUINFO* pmi);
|
||||
typedef int (*AddMRUStringFn)(HANDLE hMRU, LPCWSTR data);
|
||||
typedef int (*EnumMRUListFn)(HANDLE hMRU, int nItem, void* lpData, UINT uLen);
|
||||
typedef int (*FreeMRUListFn)(HANDLE hMRU);
|
||||
private:
|
||||
void Load();
|
||||
void Save();
|
||||
void MigrateFromRegistry();
|
||||
json::JsonArray Serialize();
|
||||
void ParseJson();
|
||||
|
||||
bool Exists(const std::wstring& data);
|
||||
|
||||
std::vector<std::wstring> items;
|
||||
long pushIdx;
|
||||
long nextIdx;
|
||||
long size;
|
||||
const std::wstring jsonFilePath;
|
||||
const std::wstring registryFilePath;
|
||||
};
|
||||
|
||||
|
||||
void MRUListHandler::Push(const std::wstring& data)
|
||||
{
|
||||
if (Exists(data))
|
||||
{
|
||||
// TODO: Already existing item should be put on top of MRU list.
|
||||
return;
|
||||
}
|
||||
items[pushIdx] = data;
|
||||
pushIdx = (pushIdx + 1) % size;
|
||||
Save();
|
||||
}
|
||||
|
||||
bool MRUListHandler::Next(std::wstring& data)
|
||||
{
|
||||
if (nextIdx == size + 1)
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
// Go backwards to consume latest items first.
|
||||
long idx = (pushIdx + size - nextIdx) % size;
|
||||
if (items[idx].empty())
|
||||
{
|
||||
Reset();
|
||||
return false;
|
||||
}
|
||||
data = items[idx];
|
||||
++nextIdx;
|
||||
return true;
|
||||
}
|
||||
|
||||
void MRUListHandler::Reset()
|
||||
{
|
||||
nextIdx = 1;
|
||||
}
|
||||
|
||||
void MRUListHandler::Load()
|
||||
{
|
||||
if (!std::filesystem::exists(jsonFilePath))
|
||||
{
|
||||
MigrateFromRegistry();
|
||||
|
||||
Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseJson();
|
||||
}
|
||||
}
|
||||
|
||||
void MRUListHandler::Save()
|
||||
{
|
||||
json::JsonObject jsonData;
|
||||
|
||||
jsonData.SetNamedValue(c_maxMRUSize, json::value(size));
|
||||
jsonData.SetNamedValue(c_insertionIdx, json::value(pushIdx));
|
||||
jsonData.SetNamedValue(c_mruList, Serialize());
|
||||
|
||||
json::to_file(jsonFilePath, jsonData);
|
||||
}
|
||||
|
||||
json::JsonArray MRUListHandler::Serialize()
|
||||
{
|
||||
json::JsonArray searchMRU{};
|
||||
|
||||
std::wstring data{};
|
||||
for (const std::wstring& item : items)
|
||||
{
|
||||
searchMRU.Append(json::value(item));
|
||||
}
|
||||
|
||||
return searchMRU;
|
||||
}
|
||||
|
||||
void MRUListHandler::MigrateFromRegistry()
|
||||
{
|
||||
std::wstring searchListKeys = GetRegString(c_mruList, registryFilePath);
|
||||
std::sort(std::begin(searchListKeys), std::end(searchListKeys));
|
||||
for (const wchar_t& key : searchListKeys)
|
||||
{
|
||||
Push(GetRegString(std::wstring(1, key), registryFilePath));
|
||||
}
|
||||
}
|
||||
|
||||
void MRUListHandler::ParseJson()
|
||||
{
|
||||
auto json = json::from_file(jsonFilePath);
|
||||
if (json)
|
||||
{
|
||||
const json::JsonObject& jsonObject = json.value();
|
||||
try
|
||||
{
|
||||
long oldSize{ size };
|
||||
if (json::has(jsonObject, c_maxMRUSize, json::JsonValueType::Number))
|
||||
{
|
||||
oldSize = (long)jsonObject.GetNamedNumber(c_maxMRUSize);
|
||||
}
|
||||
long oldPushIdx{ 0 };
|
||||
if (json::has(jsonObject, c_insertionIdx, json::JsonValueType::Number))
|
||||
{
|
||||
oldPushIdx = (long)jsonObject.GetNamedNumber(c_insertionIdx);
|
||||
if (oldPushIdx < 0 || oldPushIdx >= oldSize)
|
||||
{
|
||||
oldPushIdx = 0;
|
||||
}
|
||||
}
|
||||
if (json::has(jsonObject, c_mruList, json::JsonValueType::Array))
|
||||
{
|
||||
auto jsonArray = jsonObject.GetNamedArray(c_mruList);
|
||||
if (oldSize == size)
|
||||
{
|
||||
for (uint32_t i = 0; i < jsonArray.Size(); ++i)
|
||||
{
|
||||
items[i] = std::wstring(jsonArray.GetStringAt(i));
|
||||
}
|
||||
pushIdx = oldPushIdx;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<std::wstring> temp;
|
||||
for (uint32_t i = 0; i < min(jsonArray.Size(), size); ++i)
|
||||
{
|
||||
int idx = (oldPushIdx + oldSize - (i + 1)) % oldSize;
|
||||
temp.push_back(std::wstring(jsonArray.GetStringAt(idx)));
|
||||
}
|
||||
if (size > oldSize)
|
||||
{
|
||||
std::reverse(std::begin(temp), std::end(temp));
|
||||
pushIdx = (long)temp.size();
|
||||
temp.resize(size);
|
||||
}
|
||||
else
|
||||
{
|
||||
temp.resize(size);
|
||||
std::reverse(std::begin(temp), std::end(temp));
|
||||
}
|
||||
items = std::move(temp);
|
||||
Save();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error&) { }
|
||||
}
|
||||
}
|
||||
|
||||
bool MRUListHandler::Exists(const std::wstring& data)
|
||||
{
|
||||
return std::find(std::begin(items), std::end(items), data) != std::end(items);
|
||||
}
|
||||
|
||||
class CRenameMRU :
|
||||
public IEnumString,
|
||||
@@ -201,65 +289,33 @@ public:
|
||||
// IPowerRenameMRU
|
||||
IFACEMETHODIMP AddMRUString(_In_ PCWSTR entry);
|
||||
|
||||
static HRESULT CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize, _Outptr_ IUnknown** ppUnk);
|
||||
static HRESULT CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _Outptr_ IUnknown** ppUnk);
|
||||
|
||||
private:
|
||||
CRenameMRU();
|
||||
~CRenameMRU();
|
||||
CRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath);
|
||||
|
||||
HRESULT _Initialize(_In_ PCWSTR regPathMRU, __in ULONG maxMRUSize);
|
||||
HRESULT _CreateMRUList(_In_ MRUINFO* pmi);
|
||||
int _AddMRUString(_In_ PCWSTR data);
|
||||
int _EnumMRUList(_In_ int nItem, _Out_ void* lpData, _In_ UINT uLen);
|
||||
void _FreeMRUList();
|
||||
|
||||
long m_refCount = 0;
|
||||
HKEY m_hKey = NULL;
|
||||
ULONG m_maxMRUSize = 0;
|
||||
ULONG m_mruIndex = 0;
|
||||
ULONG m_mruSize = 0;
|
||||
HANDLE m_mruHandle = NULL;
|
||||
HMODULE m_hComctl32Dll = NULL;
|
||||
PWSTR m_regPath = nullptr;
|
||||
std::unique_ptr<MRUListHandler> mruList;
|
||||
long refCount = 0;
|
||||
};
|
||||
|
||||
CRenameMRU::CRenameMRU() :
|
||||
m_refCount(1)
|
||||
{}
|
||||
|
||||
CRenameMRU::~CRenameMRU()
|
||||
CRenameMRU::CRenameMRU(int size, const std::wstring& filePath, const std::wstring& regPath) :
|
||||
refCount(1)
|
||||
{
|
||||
if (m_hKey)
|
||||
{
|
||||
RegCloseKey(m_hKey);
|
||||
}
|
||||
|
||||
_FreeMRUList();
|
||||
|
||||
if (m_hComctl32Dll)
|
||||
{
|
||||
FreeLibrary(m_hComctl32Dll);
|
||||
}
|
||||
|
||||
CoTaskMemFree(m_regPath);
|
||||
mruList = std::make_unique<MRUListHandler>(size, filePath, regPath);
|
||||
}
|
||||
|
||||
HRESULT CRenameMRU::CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize, _Outptr_ IUnknown** ppUnk)
|
||||
HRESULT CRenameMRU::CreateInstance(_In_ const std::wstring& filePath, _In_ const std::wstring& regPath, _Outptr_ IUnknown** ppUnk)
|
||||
{
|
||||
*ppUnk = nullptr;
|
||||
HRESULT hr = (regPathMRU && maxMRUSize > 0) ? S_OK : E_FAIL;
|
||||
long maxMRUSize = CSettingsInstance().GetMaxMRUSize();
|
||||
HRESULT hr = maxMRUSize > 0 ? S_OK : E_FAIL;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
CRenameMRU* renameMRU = new CRenameMRU();
|
||||
CRenameMRU* renameMRU = new CRenameMRU(maxMRUSize, filePath, regPath);
|
||||
hr = renameMRU ? S_OK : E_OUTOFMEMORY;
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = renameMRU->_Initialize(regPathMRU, maxMRUSize);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk));
|
||||
}
|
||||
|
||||
renameMRU->QueryInterface(IID_PPV_ARGS(ppUnk));
|
||||
renameMRU->Release();
|
||||
}
|
||||
}
|
||||
@@ -267,21 +323,20 @@ HRESULT CRenameMRU::CreateInstance(_In_ PCWSTR regPathMRU, _In_ ULONG maxMRUSize
|
||||
return hr;
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHODIMP_(ULONG) CRenameMRU::AddRef()
|
||||
{
|
||||
return InterlockedIncrement(&m_refCount);
|
||||
return InterlockedIncrement(&refCount);
|
||||
}
|
||||
|
||||
IFACEMETHODIMP_(ULONG) CRenameMRU::Release()
|
||||
{
|
||||
long refCount = InterlockedDecrement(&m_refCount);
|
||||
long cnt = InterlockedDecrement(&refCount);
|
||||
|
||||
if (refCount == 0)
|
||||
if (cnt == 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
return refCount;
|
||||
return cnt;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP CRenameMRU::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
|
||||
@@ -294,57 +349,8 @@ IFACEMETHODIMP CRenameMRU::QueryInterface(_In_ REFIID riid, _Outptr_ void** ppv)
|
||||
return QISearch(this, qit, riid, ppv);
|
||||
}
|
||||
|
||||
HRESULT CRenameMRU::_Initialize(_In_ PCWSTR regPathMRU, __in ULONG maxMRUSize)
|
||||
{
|
||||
m_maxMRUSize = maxMRUSize;
|
||||
|
||||
wchar_t regPath[MAX_PATH] = { 0 };
|
||||
HRESULT hr = StringCchPrintf(regPath, ARRAYSIZE(regPath), L"%s\\%s", c_rootRegPath, regPathMRU);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = SHStrDup(regPathMRU, &m_regPath);
|
||||
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
MRUINFO mi = {
|
||||
sizeof(MRUINFO),
|
||||
maxMRUSize,
|
||||
0,
|
||||
HKEY_CURRENT_USER,
|
||||
regPath,
|
||||
nullptr
|
||||
};
|
||||
|
||||
hr = _CreateMRUList(&mi);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
m_mruSize = _EnumMRUList(-1, NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
hr = E_FAIL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
// IEnumString
|
||||
IFACEMETHODIMP CRenameMRU::Reset()
|
||||
{
|
||||
m_mruIndex = 0;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
#define MAX_ENTRY_STRING 1024
|
||||
|
||||
IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltFetched) LPOLESTR* rgelt, __out_opt ULONG* pceltFetched)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
WCHAR mruEntry[MAX_ENTRY_STRING];
|
||||
mruEntry[0] = L'\0';
|
||||
|
||||
if (pceltFetched)
|
||||
{
|
||||
*pceltFetched = 0;
|
||||
@@ -360,10 +366,10 @@ IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltF
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
hr = S_FALSE;
|
||||
if (m_mruIndex <= m_mruSize && _EnumMRUList(m_mruIndex++, (void*)mruEntry, ARRAYSIZE(mruEntry)) > 0)
|
||||
HRESULT hr = S_FALSE;
|
||||
if (std::wstring data{}; mruList->Next(data))
|
||||
{
|
||||
hr = SHStrDup(mruEntry, rgelt);
|
||||
hr = SHStrDup(data.c_str(), rgelt);
|
||||
if (SUCCEEDED(hr) && pceltFetched != nullptr)
|
||||
{
|
||||
*pceltFetched = 1;
|
||||
@@ -373,109 +379,143 @@ IFACEMETHODIMP CRenameMRU::Next(__in ULONG celt, __out_ecount_part(celt, *pceltF
|
||||
return hr;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP CRenameMRU::Reset()
|
||||
{
|
||||
mruList->Reset();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
IFACEMETHODIMP CRenameMRU::AddMRUString(_In_ PCWSTR entry)
|
||||
{
|
||||
return (_AddMRUString(entry) < 0) ? E_FAIL : S_OK;
|
||||
mruList->Push(entry);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CRenameMRU::_CreateMRUList(_In_ MRUINFO* pmi)
|
||||
CSettings::CSettings()
|
||||
{
|
||||
if (m_mruHandle != NULL)
|
||||
{
|
||||
_FreeMRUList();
|
||||
}
|
||||
|
||||
if (m_hComctl32Dll == NULL)
|
||||
{
|
||||
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
}
|
||||
|
||||
if (m_hComctl32Dll != nullptr)
|
||||
{
|
||||
CreateMRUListFn pfnCreateMRUList = reinterpret_cast<CreateMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(400)));
|
||||
if (pfnCreateMRUList != nullptr)
|
||||
{
|
||||
m_mruHandle = pfnCreateMRUList(pmi);
|
||||
}
|
||||
}
|
||||
|
||||
return (m_mruHandle != NULL) ? S_OK : E_FAIL;
|
||||
std::wstring result = PTSettingsHelper::get_module_save_folder_location(L"PowerRename");
|
||||
jsonFilePath = result + std::wstring(c_powerRenameDataFilePath);
|
||||
Load();
|
||||
}
|
||||
|
||||
int CRenameMRU::_AddMRUString(_In_ PCWSTR data)
|
||||
void CSettings::Save()
|
||||
{
|
||||
int retVal = -1;
|
||||
if (m_mruHandle != NULL)
|
||||
{
|
||||
if (m_hComctl32Dll == NULL)
|
||||
{
|
||||
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
}
|
||||
json::JsonObject jsonData;
|
||||
|
||||
if (m_hComctl32Dll != nullptr)
|
||||
jsonData.SetNamedValue(c_enabled, json::value(settings.enabled));
|
||||
jsonData.SetNamedValue(c_showIconOnMenu, json::value(settings.showIconOnMenu));
|
||||
jsonData.SetNamedValue(c_extendedContextMenuOnly, json::value(settings.extendedContextMenuOnly));
|
||||
jsonData.SetNamedValue(c_persistState, json::value(settings.persistState));
|
||||
jsonData.SetNamedValue(c_mruEnabled, json::value(settings.MRUEnabled));
|
||||
jsonData.SetNamedValue(c_maxMRUSize, json::value(settings.maxMRUSize));
|
||||
jsonData.SetNamedValue(c_flags, json::value(settings.flags));
|
||||
jsonData.SetNamedValue(c_searchText, json::value(settings.searchText));
|
||||
jsonData.SetNamedValue(c_replaceText, json::value(settings.replaceText));
|
||||
|
||||
json::to_file(jsonFilePath, jsonData);
|
||||
}
|
||||
|
||||
void CSettings::Load()
|
||||
{
|
||||
if (!std::filesystem::exists(jsonFilePath))
|
||||
{
|
||||
MigrateFromRegistry();
|
||||
|
||||
Save();
|
||||
}
|
||||
else
|
||||
{
|
||||
ParseJson();
|
||||
}
|
||||
GetSystemTimeAsFileTime(&lastLoadedTime);
|
||||
}
|
||||
|
||||
void CSettings::Reload()
|
||||
{
|
||||
// Load json settings from data file if it is modified in the meantime.
|
||||
FILETIME lastModifiedTime{};
|
||||
if (LastModifiedTime(jsonFilePath, &lastModifiedTime) &&
|
||||
CompareFileTime(&lastModifiedTime, &lastLoadedTime) == 1)
|
||||
{
|
||||
Load();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void CSettings::MigrateFromRegistry()
|
||||
{
|
||||
settings.enabled = GetRegBoolean(c_enabled, true);
|
||||
settings.showIconOnMenu = GetRegBoolean(c_showIconOnMenu, true);
|
||||
settings.extendedContextMenuOnly = GetRegBoolean(c_extendedContextMenuOnly, false); // Disabled by default.
|
||||
settings.persistState = GetRegBoolean(c_persistState, true);
|
||||
settings.MRUEnabled = GetRegBoolean(c_mruEnabled, true);
|
||||
settings.maxMRUSize = GetRegNumber(c_maxMRUSize, 10);
|
||||
settings.flags = GetRegNumber(c_flags, 0);
|
||||
settings.searchText = GetRegString(c_searchText, L"");
|
||||
settings.replaceText = GetRegString(c_replaceText, L"");
|
||||
}
|
||||
|
||||
void CSettings::ParseJson()
|
||||
{
|
||||
auto json = json::from_file(jsonFilePath);
|
||||
if (json)
|
||||
{
|
||||
const json::JsonObject& jsonSettings = json.value();
|
||||
try
|
||||
{
|
||||
AddMRUStringFn pfnAddMRUString = reinterpret_cast<AddMRUStringFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(401)));
|
||||
if (pfnAddMRUString != nullptr)
|
||||
if (json::has(jsonSettings, c_enabled, json::JsonValueType::Boolean))
|
||||
{
|
||||
retVal = pfnAddMRUString(m_mruHandle, data);
|
||||
settings.enabled = jsonSettings.GetNamedBoolean(c_enabled);
|
||||
}
|
||||
if (json::has(jsonSettings, c_showIconOnMenu, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.showIconOnMenu = jsonSettings.GetNamedBoolean(c_showIconOnMenu);
|
||||
}
|
||||
if (json::has(jsonSettings, c_extendedContextMenuOnly, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.extendedContextMenuOnly = jsonSettings.GetNamedBoolean(c_extendedContextMenuOnly);
|
||||
}
|
||||
if (json::has(jsonSettings, c_persistState, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.persistState = jsonSettings.GetNamedBoolean(c_persistState);
|
||||
}
|
||||
if (json::has(jsonSettings, c_mruEnabled, json::JsonValueType::Boolean))
|
||||
{
|
||||
settings.MRUEnabled = jsonSettings.GetNamedBoolean(c_mruEnabled);
|
||||
}
|
||||
if (json::has(jsonSettings, c_maxMRUSize, json::JsonValueType::Number))
|
||||
{
|
||||
settings.maxMRUSize = (long)jsonSettings.GetNamedNumber(c_maxMRUSize);
|
||||
}
|
||||
if (json::has(jsonSettings, c_flags, json::JsonValueType::Number))
|
||||
{
|
||||
settings.flags = (long)jsonSettings.GetNamedNumber(c_flags);
|
||||
}
|
||||
if (json::has(jsonSettings, c_searchText, json::JsonValueType::String))
|
||||
{
|
||||
settings.searchText = jsonSettings.GetNamedString(c_searchText);
|
||||
}
|
||||
if (json::has(jsonSettings, c_replaceText, json::JsonValueType::String))
|
||||
{
|
||||
settings.replaceText = jsonSettings.GetNamedString(c_replaceText);
|
||||
}
|
||||
}
|
||||
catch (const winrt::hresult_error&) { }
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
int CRenameMRU::_EnumMRUList(_In_ int nItem, _Out_ void* lpData, _In_ UINT uLen)
|
||||
CSettings& CSettingsInstance()
|
||||
{
|
||||
int retVal = -1;
|
||||
if (m_mruHandle != NULL)
|
||||
{
|
||||
if (m_hComctl32Dll == NULL)
|
||||
{
|
||||
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
}
|
||||
|
||||
if (m_hComctl32Dll != nullptr)
|
||||
{
|
||||
EnumMRUListFn pfnEnumMRUList = reinterpret_cast<EnumMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(403)));
|
||||
if (pfnEnumMRUList != nullptr)
|
||||
{
|
||||
retVal = pfnEnumMRUList(m_mruHandle, nItem, lpData, uLen);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
void CRenameMRU::_FreeMRUList()
|
||||
{
|
||||
if (m_mruHandle != NULL)
|
||||
{
|
||||
if (m_hComctl32Dll == NULL)
|
||||
{
|
||||
m_hComctl32Dll = LoadLibraryEx(L"comctl32.dll", nullptr, LOAD_LIBRARY_SEARCH_SYSTEM32);
|
||||
}
|
||||
|
||||
if (m_hComctl32Dll != nullptr)
|
||||
{
|
||||
FreeMRUListFn pfnFreeMRUList = reinterpret_cast<FreeMRUListFn>(GetProcAddress(m_hComctl32Dll, (LPCSTR)MAKEINTRESOURCE(152)));
|
||||
if (pfnFreeMRUList != nullptr)
|
||||
{
|
||||
pfnFreeMRUList(m_mruHandle);
|
||||
}
|
||||
|
||||
}
|
||||
m_mruHandle = NULL;
|
||||
}
|
||||
static CSettings instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk)
|
||||
{
|
||||
return CRenameMRU::CreateInstance(c_mruSearchRegPath, CSettings::GetMaxMRUSize(), ppUnk);
|
||||
return CRenameMRU::CreateInstance(c_searchMRUListFilePath, c_mruSearchRegPath, ppUnk);
|
||||
}
|
||||
|
||||
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk)
|
||||
{
|
||||
return CRenameMRU::CreateInstance(c_mruReplaceRegPath, CSettings::GetMaxMRUSize(), ppUnk);
|
||||
return CRenameMRU::CreateInstance(c_replaceMRUListFilePath, c_mruReplaceRegPath, ppUnk);
|
||||
}
|
||||
|
||||
@@ -1,45 +1,136 @@
|
||||
#pragma once
|
||||
|
||||
#include "json.h"
|
||||
|
||||
class CSettings
|
||||
{
|
||||
public:
|
||||
static const int MAX_INPUT_STRING_LEN = 1024;
|
||||
|
||||
static bool GetEnabled();
|
||||
static bool SetEnabled(_In_ bool enabled);
|
||||
CSettings();
|
||||
|
||||
static bool GetShowIconOnMenu();
|
||||
static bool SetShowIconOnMenu(_In_ bool show);
|
||||
inline bool GetEnabled()
|
||||
{
|
||||
Reload();
|
||||
return settings.enabled;
|
||||
}
|
||||
|
||||
static bool GetExtendedContextMenuOnly();
|
||||
static bool SetExtendedContextMenuOnly(_In_ bool extendedOnly);
|
||||
inline void SetEnabled(bool enabled)
|
||||
{
|
||||
settings.enabled = enabled;
|
||||
Save();
|
||||
}
|
||||
|
||||
static bool GetPersistState();
|
||||
static bool SetPersistState(_In_ bool extendedOnly);
|
||||
inline bool GetShowIconOnMenu() const
|
||||
{
|
||||
return settings.showIconOnMenu;
|
||||
}
|
||||
|
||||
static bool GetMRUEnabled();
|
||||
static bool SetMRUEnabled(_In_ bool enabled);
|
||||
inline void SetShowIconOnMenu(bool show)
|
||||
{
|
||||
settings.showIconOnMenu = show;
|
||||
}
|
||||
|
||||
static DWORD GetMaxMRUSize();
|
||||
static bool SetMaxMRUSize(_In_ DWORD maxMRUSize);
|
||||
inline bool GetExtendedContextMenuOnly() const
|
||||
{
|
||||
return settings.extendedContextMenuOnly;
|
||||
}
|
||||
|
||||
static DWORD GetFlags();
|
||||
static bool SetFlags(_In_ DWORD flags);
|
||||
inline void SetExtendedContextMenuOnly(bool extendedOnly)
|
||||
{
|
||||
settings.extendedContextMenuOnly = extendedOnly;
|
||||
}
|
||||
|
||||
static bool GetSearchText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
|
||||
static bool SetSearchText(_In_ PCWSTR text);
|
||||
inline bool GetPersistState() const
|
||||
{
|
||||
return settings.persistState;
|
||||
}
|
||||
|
||||
static bool GetReplaceText(__out_ecount(cchBuf) PWSTR text, DWORD cchBuf);
|
||||
static bool SetReplaceText(_In_ PCWSTR text);
|
||||
inline void SetPersistState(bool persistState)
|
||||
{
|
||||
settings.persistState = persistState;
|
||||
}
|
||||
|
||||
inline bool GetMRUEnabled() const
|
||||
{
|
||||
return settings.MRUEnabled;
|
||||
}
|
||||
|
||||
inline void SetMRUEnabled(bool MRUEnabled)
|
||||
{
|
||||
settings.MRUEnabled = MRUEnabled;
|
||||
}
|
||||
|
||||
inline long GetMaxMRUSize() const
|
||||
{
|
||||
return settings.maxMRUSize;
|
||||
}
|
||||
|
||||
inline void SetMaxMRUSize(long maxMRUSize)
|
||||
{
|
||||
settings.maxMRUSize = maxMRUSize;
|
||||
}
|
||||
|
||||
inline long GetFlags() const
|
||||
{
|
||||
return settings.flags;
|
||||
}
|
||||
|
||||
inline void SetFlags(long flags)
|
||||
{
|
||||
settings.flags = flags;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline const std::wstring& GetSearchText() const
|
||||
{
|
||||
return settings.searchText;
|
||||
}
|
||||
|
||||
inline void SetSearchText(const std::wstring& text)
|
||||
{
|
||||
settings.searchText = text;
|
||||
Save();
|
||||
}
|
||||
|
||||
inline const std::wstring& GetReplaceText() const
|
||||
{
|
||||
return settings.replaceText;
|
||||
}
|
||||
|
||||
inline void SetReplaceText(const std::wstring& text)
|
||||
{
|
||||
settings.replaceText = text;
|
||||
Save();
|
||||
}
|
||||
|
||||
void Save();
|
||||
void Load();
|
||||
|
||||
private:
|
||||
static bool GetRegBoolValue(_In_ PCWSTR valueName, _In_ bool defaultValue);
|
||||
static bool SetRegBoolValue(_In_ PCWSTR valueName, _In_ bool value);
|
||||
static bool SetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD value);
|
||||
static DWORD GetRegDWORDValue(_In_ PCWSTR valueName, _In_ DWORD defaultValue);
|
||||
static bool SetRegStringValue(_In_ PCWSTR valueName, _In_ PCWSTR value);
|
||||
static bool GetRegStringValue(_In_ PCWSTR valueName, __out_ecount(cchBuf) PWSTR value, DWORD cchBuf);
|
||||
struct Settings
|
||||
{
|
||||
bool enabled{ true };
|
||||
bool showIconOnMenu{ true };
|
||||
bool extendedContextMenuOnly{ false }; // Disabled by default.
|
||||
bool persistState{ true };
|
||||
bool MRUEnabled{ true };
|
||||
long maxMRUSize{ 10 };
|
||||
long flags{ 0 };
|
||||
std::wstring searchText{};
|
||||
std::wstring replaceText{};
|
||||
};
|
||||
|
||||
void Reload();
|
||||
void MigrateFromRegistry();
|
||||
void ParseJson();
|
||||
|
||||
Settings settings;
|
||||
std::wstring jsonFilePath;
|
||||
FILETIME lastLoadedTime;
|
||||
};
|
||||
|
||||
CSettings& CSettingsInstance();
|
||||
|
||||
HRESULT CRenameMRUSearch_CreateInstance(_Outptr_ IUnknown** ppUnk);
|
||||
HRESULT CRenameMRUReplace_CreateInstance(_Outptr_ IUnknown** ppUnk);
|
||||
@@ -19,3 +19,5 @@
|
||||
#include <shlwapi.h>
|
||||
|
||||
#include <ProjectTelemetry.h>
|
||||
|
||||
#pragma comment(lib, "windowsapp")
|
||||
|
||||
@@ -79,11 +79,11 @@ void Trace::SettingsChanged() noexcept
|
||||
"PowerRename_SettingsChanged",
|
||||
ProjectTelemetryPrivacyDataTag(ProjectTelemetryTag_ProductAndServicePerformance),
|
||||
TraceLoggingKeyword(PROJECT_KEYWORD_MEASURE),
|
||||
TraceLoggingBoolean(CSettings::GetEnabled(), "IsEnabled"),
|
||||
TraceLoggingBoolean(CSettings::GetShowIconOnMenu(), "ShowIconOnMenu"),
|
||||
TraceLoggingBoolean(CSettings::GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
|
||||
TraceLoggingBoolean(CSettings::GetPersistState(), "PersistState"),
|
||||
TraceLoggingBoolean(CSettings::GetMRUEnabled(), "IsMRUEnabled"),
|
||||
TraceLoggingUInt64(CSettings::GetMaxMRUSize(), "MaxMRUSize"),
|
||||
TraceLoggingUInt64(CSettings::GetFlags(), "Flags"));
|
||||
TraceLoggingBoolean(CSettingsInstance().GetEnabled(), "IsEnabled"),
|
||||
TraceLoggingBoolean(CSettingsInstance().GetShowIconOnMenu(), "ShowIconOnMenu"),
|
||||
TraceLoggingBoolean(CSettingsInstance().GetExtendedContextMenuOnly(), "ExtendedContextMenuOnly"),
|
||||
TraceLoggingBoolean(CSettingsInstance().GetPersistState(), "PersistState"),
|
||||
TraceLoggingBoolean(CSettingsInstance().GetMRUEnabled(), "IsMRUEnabled"),
|
||||
TraceLoggingUInt64(CSettingsInstance().GetMaxMRUSize(), "MaxMRUSize"),
|
||||
TraceLoggingUInt64(CSettingsInstance().GetFlags(), "Flags"));
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
#include <commctrl.h>
|
||||
#include <Shlobj.h>
|
||||
#include <helpers.h>
|
||||
#include <settings.h>
|
||||
#include <windowsx.h>
|
||||
#include <thread>
|
||||
#include <trace.h>
|
||||
@@ -80,6 +79,14 @@ inline int RECT_HEIGHT(RECT& r)
|
||||
return r.bottom - r.top;
|
||||
}
|
||||
|
||||
CPowerRenameUI::CPowerRenameUI() :
|
||||
m_refCount(1)
|
||||
{
|
||||
CSettingsInstance().Load();
|
||||
(void)OleInitialize(nullptr);
|
||||
ModuleAddRef();
|
||||
}
|
||||
|
||||
// IUnknown
|
||||
IFACEMETHODIMP CPowerRenameUI::QueryInterface(__in REFIID riid, __deref_out void** ppv)
|
||||
{
|
||||
@@ -308,7 +315,7 @@ HRESULT CPowerRenameUI::_Initialize(_In_ IPowerRenameManager* psrm, _In_opt_ IUn
|
||||
HRESULT CPowerRenameUI::_InitAutoComplete()
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
if (CSettings::GetMRUEnabled())
|
||||
if (CSettingsInstance().GetMRUEnabled())
|
||||
{
|
||||
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC, IID_PPV_ARGS(&m_spSearchAC));
|
||||
if (SUCCEEDED(hr))
|
||||
@@ -387,19 +394,13 @@ HRESULT CPowerRenameUI::_ReadSettings()
|
||||
// Check if we should read flags from settings
|
||||
// or the defaults from the manager.
|
||||
DWORD flags = 0;
|
||||
if (CSettings::GetPersistState())
|
||||
if (CSettingsInstance().GetPersistState())
|
||||
{
|
||||
flags = CSettings::GetFlags();
|
||||
flags = CSettingsInstance().GetFlags();
|
||||
m_spsrm->put_flags(flags);
|
||||
|
||||
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
|
||||
buffer[0] = L'\0';
|
||||
CSettings::GetSearchText(buffer, ARRAYSIZE(buffer));
|
||||
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer);
|
||||
|
||||
buffer[0] = L'\0';
|
||||
CSettings::GetReplaceText(buffer, ARRAYSIZE(buffer));
|
||||
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer);
|
||||
SetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, CSettingsInstance().GetSearchText().c_str());
|
||||
SetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, CSettingsInstance().GetReplaceText().c_str());
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -414,18 +415,18 @@ HRESULT CPowerRenameUI::_ReadSettings()
|
||||
HRESULT CPowerRenameUI::_WriteSettings()
|
||||
{
|
||||
// Check if we should store our settings
|
||||
if (CSettings::GetPersistState())
|
||||
if (CSettingsInstance().GetPersistState())
|
||||
{
|
||||
DWORD flags = 0;
|
||||
m_spsrm->get_flags(&flags);
|
||||
CSettings::SetFlags(flags);
|
||||
CSettingsInstance().SetFlags(flags);
|
||||
|
||||
wchar_t buffer[CSettings::MAX_INPUT_STRING_LEN];
|
||||
buffer[0] = L'\0';
|
||||
GetDlgItemText(m_hwnd, IDC_EDIT_SEARCHFOR, buffer, ARRAYSIZE(buffer));
|
||||
CSettings::SetSearchText(buffer);
|
||||
CSettingsInstance().SetSearchText(buffer);
|
||||
|
||||
if (CSettings::GetMRUEnabled() && m_spSearchACL)
|
||||
if (CSettingsInstance().GetMRUEnabled() && m_spSearchACL)
|
||||
{
|
||||
CComPtr<IPowerRenameMRU> spSearchMRU;
|
||||
if (SUCCEEDED(m_spSearchACL->QueryInterface(IID_PPV_ARGS(&spSearchMRU))))
|
||||
@@ -436,9 +437,9 @@ HRESULT CPowerRenameUI::_WriteSettings()
|
||||
|
||||
buffer[0] = L'\0';
|
||||
GetDlgItemText(m_hwnd, IDC_EDIT_REPLACEWITH, buffer, ARRAYSIZE(buffer));
|
||||
CSettings::SetReplaceText(buffer);
|
||||
CSettingsInstance().SetReplaceText(buffer);
|
||||
|
||||
if (CSettings::GetMRUEnabled() && m_spReplaceACL)
|
||||
if (CSettingsInstance().GetMRUEnabled() && m_spReplaceACL)
|
||||
{
|
||||
CComPtr<IPowerRenameMRU> spReplaceMRU;
|
||||
if (SUCCEEDED(m_spReplaceACL->QueryInterface(IID_PPV_ARGS(&spReplaceMRU))))
|
||||
|
||||